otsu 應用
依據像素值的權重,分離圖像前景和背景。
otsu 算法确定圖像前景和背景門檻值
// 關鍵之處是處理像素值的權重占比
// Otsu algorithm
int Otsu(const Mat src)
{
int height = src.rows;
int width = src.cols;
int size = height * width;
unsigned char* data = src.data;
// histogram
long histogram[] = { };
// pixel gray value count
for (int i = ; i < height; i++)
{
unsigned char* p = data + i * src.step;
for (int j = ; j < width; j++)
{
histogram[int(*p++)]++;
}
}
/*
* sum0:前景的灰階總和 sum1:背景灰階總和
* cnt0:前景像素的總個數 cnt1:背景像素的總個數
* w0: 前景像素個數占整幅圖像像素的比例
* w1: 背景像素個數占整幅圖像像素的比例
* u0: 前景的平均灰階 u1: 背景的平均灰階
* variance: 類間方差
*/
long sum0 = , sum1 = ;
long cnt0 = , cnt1 = ;
double w0 = , w1 = ;
double u0 = , u1 = ;
double variance = ;
/*
* u: 整幅圖像的總平均灰階
* maxVariance: 最大類間方差
*/
int i, j;
double u = ;
double maxVariance = ;
// 目标門檻值
int threshold = ;
// 依次周遊每個像素
for (i = ; i < ; i++)
{
// 初始化
sum0 = ; sum1 = ;
cnt0 = ; cnt1 = ;
u0 = ; u1 = ;
w0 = ; w1 = ;
// 前景
for (j = ; j < i; j++)
{
cnt0 += histogram[j];
sum0 += (long)(j * histogram[j]);
}
// u0:前景平均灰階 w0:前景像素點數量占全部像素點的比例
u0 = (double)(sum0 * / cnt0);
w0 = (double)(cnt0 * / size);
// 背景
for (j = i; j <= ; j++)
{
cnt1 += histogram[j];
sum1 += (long)(j * histogram[j]);
}
// u1:背景平均灰階 w1:背景像素點數占全部像素點的比例
u1 = (double)(sum1 * / cnt1);
w1 = - w0;
// u:圖像平均灰階 variancn:類間方差
//u = u0 * w0 + u1 * w1;
variance = w0 * w1 * (u0 - u1) * (u0 - u1);
if (variance > maxVariance)
{
maxVariance = variance;
threshold = i;
}
}
return (threshold);
}
分别根據r,g,b三通道的門檻值分離圖像,再merge(r,g,b)
- 圖像分離可采用 opencv split()函數,将Mat格式圖像轉換為vector格式的圖像。
- 合并r, g, b則采用相反的操作,使用opencv merge()函數,将vector格式的圖像合并成Mat格式的完整圖像。
// splite input image
void ThresholdByOtsu(const Mat src, Mat & Dst)
{
// split Mat src to Vector src_v
vector<Mat>src_v;
split(src, src_v);
imshow("r_src", src_v[]);
imshow("g_src", src_v[]);
imshow("b_src", src_v[]);
// save origin image r, g, b
imwrite((g_kOutputPath + "r_src.tiff"), src_v[]);
imwrite((g_kOutputPath + "g_src.tiff"), src_v[]);
imwrite((g_kOutputPath + "b_src.tiff"), src_v[]);
// get threshold
int threshold_r, threshold_g, threshold_b = ;
threshold_r = Otsu(src_v[]);
threshold_g = Otsu(src_v[]);
threshold_b = Otsu(src_v[]);
// output r, g, b threshold after otsu.
cout << "after otsu:" << endl;
cout << "------------------" << endl;
cout << "threshold_r:" << threshold_r << endl;
cout << "threshold_g:" << threshold_g << endl;
cout << "threshold_b:" << threshold_b << endl;
// 二值化
vector<Mat>out_v;
threshold(src_v[], src_v[], threshold_r, , CV_THRESH_BINARY);
threshold(src_v[], src_v[], threshold_g, , CV_THRESH_BINARY);
threshold(src_v[], src_v[], threshold_b, , CV_THRESH_BINARY);
// save r, g, b image after otsu
imwrite((g_kOutputPath + "r_otsu.tiff"), src_v[]);
imwrite((g_kOutputPath + "g_otsu.tiff"), src_v[]);
imwrite((g_kOutputPath + "b_otsu.tiff.tiff"), src_v[]);
// merge three chanels image with r_otsu, g_otsu, b_otsu
merge(src_v, Dst);
// clear
src_v.clear();
}
Example
輸入圖像: http://sipi.usc.edu/database/download.php?vol=misc&img=4.2.04
1. 美麗的Lena女士
2. 原圖像 R, G, B 三通道分離,依次輸出r,g,b單通道圖像如下
3. Otsu() 分别計算圖像門檻值得到r, g, b單通道門檻值
channel | threshold |
---|---|
r | 113 |
g | 102 |
b | 162 |
3. 原圖像R,G,B三通道,otsu門檻值二值化,依次輸出如下圖像
4. 将二值化的R,G,B三通道進行merge操作,輸出OTSU的最終結果
編譯環境
dev | version |
---|---|
os | window10 |
image library | opencv3.2.0 |
compiler | Virtual Studio 2015 |
More
(全文完)