Java基於opencv實現圖像數字識別(三)—灰度化和二值化
一、灰度化
灰度化:在RGB模型中,如果R=G=B時,則彩色表示灰度顏色,其中R=G=B的值叫灰度值;因此,灰度圖像每個像素點只需一個字節存放灰度值(又稱強度值、亮度值),灰度范圍為0-255。一般常用的是加權平均法來求像素點的灰度值,opencv開發庫所采用的一種求灰度值算法如下;
:)Gray = 0.072169 * B + 0.715160 * G + 0.212671 * R
有兩種方式可以實現灰度化,如下
方式1
@Test
public void toGray() {
// 這個必須要寫,不寫報java.lang.UnsatisfiedLinkError
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
File imgFile = new File("C:/Users/admin/Desktop/open/test.png");
String dest = "C:/Users/admin/Desktop/open";
//方式一
Mat src = Imgcodecs.imread(imgFile.toString(), Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE);
//保存灰度化的圖片
Imgcodecs.imwrite(dest + "/toGray" + imgFile.getName(), src);
}
方式2
@Test
public void toGray() {
// 這個必須要寫,不寫報java.lang.UnsatisfiedLinkError
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
File imgFile = new File("C:/Users/admin/Desktop/open/test.png");
String dest = "C:/Users/admin/Desktop/open";
//方式二
Mat src = Imgcodecs.imread(imgFile.toString());
Mat gray = new Mat();
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
src = gray;
//保存灰度化的圖片
Imgcodecs.imwrite(dest + "/toGray2" + imgFile.getName(), src);
}
二值化:圖像的二值化,就是將圖像上的像素點的灰度值設置位0或255這兩個極點,也就是將整個圖像呈現出明顯的只有黑和白的視覺效果
常見的二值化方法為固定閥值和自適應閥值,固定閥值就是制定一個固定的數值作為分界點,大於這個閥值的像素就設為255,小於該閥值就設為0,這種方法簡單粗暴,但是效果不一定好.另外就是自適應閥值,每次根據圖片的灰度情況找合適的閥值。自適應閥值的方法有很多,這里采用了一種類似K均值的方法,就是先選擇一個值作為閥值,統計大於這個閥值的所有像素的灰度平均值和小於這個閥值的所有像素的灰度平均值,再求這兩個值的平均值作為新的閥值。重復上面的計算,直到每次更新閥值后,大於該閥值和小於該閥值的像素數目不變為止。
代碼如下
@Test
public void binaryzation(Mat mat) {
int BLACK = 0;
int WHITE = 255;
int ucThre = 0, ucThre_new = 127;
int nBack_count, nData_count;
int nBack_sum, nData_sum;
int nValue;
int i, j;
int width = mat.width(), height = mat.height();
//尋找最佳的闕值
while (ucThre != ucThre_new) {
nBack_sum = nData_sum = 0;
nBack_count = nData_count = 0;
for (j = 0; j < height; ++j) {
for (i = 0; i < width; i++) {
nValue = (int) mat.get(j, i)[0];
if (nValue > ucThre_new) {
nBack_sum += nValue;
nBack_count++;
} else {
nData_sum += nValue;
nData_count++;
}
}
}
nBack_sum = nBack_sum / nBack_count;
nData_sum = nData_sum / nData_count;
ucThre = ucThre_new;
ucThre_new = (nBack_sum + nData_sum) / 2;
}
//二值化處理
int nBlack = 0;
int nWhite = 0;
for (j = 0; j < height; ++j) {
for (i = 0; i < width; ++i) {
nValue = (int) mat.get(j, i)[0];
if (nValue > ucThre_new) {
mat.put(j, i, WHITE);
nWhite++;
} else {
mat.put(j, i, BLACK);
nBlack++;
}
}
}
// 確保白底黑字
if (nBlack > nWhite) {
for (j = 0; j < height; ++j) {
for (i = 0; i < width; ++i) {
nValue = (int) (mat.get(j, i)[0]);
if (nValue == 0) {
mat.put(j, i, WHITE);
} else {
mat.put(j, i, BLACK);
}
}
}
}
}
測試二值化
@Test
public void binaryzation() {
// 這個必須要寫,不寫報java.lang.UnsatisfiedLinkError
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
File imgFile = new File("C:/Users/admin/Desktop/open/test.png");
String dest = "C:/Users/admin/Desktop/open";
//先經過一步灰度化
Mat src = Imgcodecs.imread(imgFile.toString());
Mat gray = new Mat();
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
src = gray;
//二值化
binaryzation(src);
Imgcodecs.imwrite(dest + "/binaryzation" + imgFile.getName(), src);
}
Opencv自己也提供了二值化的接口,好像沒有上面的效果好,這里也把代碼放出來
@Test
public void testOpencvBinary() {
// 這個必須要寫,不寫報java.lang.UnsatisfiedLinkError
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
File imgFile = new File("C:/Users/admin/Desktop/open/test.png");
String dest = "C:/Users/admin/Desktop/open";
Mat src = Imgcodecs.imread(imgFile.toString(), Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE);
Imgcodecs.imwrite(dest + "/AdaptiveThreshold1" + imgFile.getName(), src);
Mat dst = new Mat();
Imgproc.adaptiveThreshold(src, dst, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 13, 5);
Imgcodecs.imwrite(dest + "/AdaptiveThreshold2" + imgFile.getName(), dst);
Imgproc.adaptiveThreshold(src, dst, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY_INV, 13, 5);
Imgcodecs.imwrite(dest + "/AdaptiveThreshold3" + imgFile.getName(), dst);
Imgproc.adaptiveThreshold(src, dst, 255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY, 13, 5);
Imgcodecs.imwrite(dest + "/AdaptiveThreshold4" + imgFile.getName(), dst);
Imgproc.adaptiveThreshold(src, dst, 255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY_INV, 13, 5);
Imgcodecs.imwrite(dest + "/AdaptiveThreshold5" + imgFile.getName(), dst);
}
本文章參考了很多博客,感謝;主要是跟着一個博客來實現的https://blog.csdn.net/ysc6688/article/category/2913009(也是基於opencv來做的,只不過他是用c++實現的)感謝