Java基于opencv實(shí)現(xiàn)圖像數(shù)字識(shí)別(四)—圖像降噪
我們每一步的工作都是基于前一步的,我們先把我們前面的幾個(gè)函數(shù)封裝成一個(gè)工具類段多,以后我們所有的函數(shù)都基于這個(gè)工具類
這個(gè)工具類呢轮傍,就一個(gè)成員變量Mat,非常的簡(jiǎn)單氓栈,這里給出代碼
public class ImageUtils {
private static final int BLACK = 0;
private static final int WHITE = 255;
private Mat mat;
/**
* 空參構(gòu)造函數(shù)
*/
public ImageUtils() {
}
/**
* 通過(guò)圖像路徑創(chuàng)建一個(gè)mat矩陣
*
* @param imgFilePath
* 圖像路徑
*/
public ImageUtils(String imgFilePath) {
mat = Imgcodecs.imread(imgFilePath);
}
public void ImageUtils(Mat mat) {
this.mat = mat;
}
/**
* 加載圖片
*
* @param imgFilePath
*/
public void loadImg(String imgFilePath) {
mat = Imgcodecs.imread(imgFilePath);
}
/**
* 獲取圖片高度的函數(shù)
*
* @return
*/
public int getHeight() {
return mat.rows();
}
/**
* 獲取圖片寬度的函數(shù)
*
* @return
*/
public int getWidth() {
return mat.cols();
}
/**
* 獲取圖片像素點(diǎn)的函數(shù)
*
* @param y
* @param x
* @return
*/
public int getPixel(int y, int x) {
// 我們處理的是單通道灰度圖
return (int) mat.get(y, x)[0];
}
/**
* 設(shè)置圖片像素點(diǎn)的函數(shù)
*
* @param y
* @param x
* @param color
*/
public void setPixel(int y, int x, int color) {
// 我們處理的是單通道灰度圖
mat.put(y, x, color);
}
/**
* 保存圖片的函數(shù)
*
* @param filename
* @return
*/
public boolean saveImg(String filename) {
return Imgcodecs.imwrite(filename, mat);
}
}
灰度化和二值化的代碼我沒(méi)有貼出來(lái)彬犯,因?yàn)榇a實(shí)在有點(diǎn)長(zhǎng)
我們接著上一步的成果,來(lái)開(kāi)始我們的降噪
一田炭、8鄰域降噪
我感覺(jué)9宮格降噪更形象一點(diǎn)师抄;即9宮格中心被異色包圍,則同化
降噪效果還是蠻好的教硫,這個(gè)方法對(duì)小噪點(diǎn)比較好
/**
* 8鄰域降噪,又有點(diǎn)像9宮格降噪;即如果9宮格中心被異色包圍叨吮,則同化
* @param pNum 默認(rèn)值為1
*/
public void navieRemoveNoise(int pNum) {
int i, j, m, n, nValue, nCount;
int nWidth = getWidth(), nHeight = getHeight();
// 對(duì)圖像的邊緣進(jìn)行預(yù)處理
for (i = 0; i < nWidth; ++i) {
setPixel(i, 0, WHITE);
setPixel(i, nHeight - 1, WHITE);
}
for (i = 0; i < nHeight; ++i) {
setPixel(0, i, WHITE);
setPixel(nWidth - 1, i, WHITE);
}
// 如果一個(gè)點(diǎn)的周?chē)际前咨模_是黑色的瞬矩,刪除它
for (j = 1; j < nHeight - 1; ++j) {
for (i = 1; i < nWidth - 1; ++i) {
nValue = getPixel(j, i);
if (nValue == 0) {
nCount = 0;
// 比較以(j ,i)為中心的9宮格茶鉴,如果周?chē)际前咨模? for (m = j - 1; m <= j + 1; ++m) {
for (n = i - 1; n <= i + 1; ++n) {
if (getPixel(m, n) == 0) {
nCount++;
}
}
}
if (nCount <= pNum) {
// 周?chē)谏c(diǎn)的個(gè)數(shù)小于閥值pNum,把該點(diǎn)設(shè)置白色
setPixel(j, i, WHITE);
}
} else {
nCount = 0;
// 比較以(j ,i)為中心的9宮格景用,如果周?chē)际呛谏暮#? for (m = j - 1; m <= j + 1; ++m) {
for (n = i - 1; n <= i + 1; ++n) {
if (getPixel(m, n) == 0) {
nCount++;
}
}
}
if (nCount >= 7) {
// 周?chē)谏c(diǎn)的個(gè)數(shù)大于等于7,把該點(diǎn)設(shè)置黑色;即周?chē)际呛谏? setPixel(j, i, BLACK);
}
}
}
}
}
二、連通域降噪
我們先介紹一個(gè)函數(shù)(floodFill)
floodFill就是把一個(gè)點(diǎn)x的所有相鄰的點(diǎn)都涂上x(chóng)點(diǎn)的顏色伞插,一直填充下去割粮,直到這個(gè)區(qū)域內(nèi)所有的點(diǎn)都被填充完為止
在計(jì)算的過(guò)程中,每掃描到一個(gè)黑色(灰度值為0)的點(diǎn)媚污,就將與該點(diǎn)連通的所有點(diǎn)的灰度值都改為1舀瓢,因此這一個(gè)連通域的點(diǎn)都不會(huì)再次重復(fù)計(jì)算了。下一個(gè)灰度值為0的點(diǎn)所有連通點(diǎn)的顏色都改為2耗美,這樣依次遞加京髓,直到所有的點(diǎn)都掃描完。接下來(lái)再次掃描所有的點(diǎn)商架,統(tǒng)計(jì)每一個(gè)灰度值對(duì)應(yīng)的點(diǎn)的個(gè)數(shù)堰怨,每一個(gè)灰度值的點(diǎn)的個(gè)數(shù)對(duì)應(yīng)該連通域的大小,并且不同連通域由于灰度值不同甸私,因此每個(gè)點(diǎn)只計(jì)算一次诚些,不會(huì)重復(fù)飞傀。這樣一來(lái)就統(tǒng)計(jì)到了每個(gè)連通域的大小皇型,再根據(jù)預(yù)設(shè)的閥值,如果該連通域大小小于閥值砸烦,則其就為噪點(diǎn)弃鸦。這個(gè)算法比較適合檢查大的噪點(diǎn),與上個(gè)算法正好相反幢痘。
因?yàn)槲艺业膱D像關(guān)系唬格,效果可能不咋明顯;
/**
* 連通域降噪
* @param pArea 默認(rèn)值為1
*/
public void contoursRemoveNoise(double pArea) {
int i, j, color = 1;
int nWidth = getWidth(), nHeight = getHeight();
for (i = 0; i < nWidth; ++i) {
for (j = 0; j < nHeight; ++j) {
if (getPixel(j, i) == BLACK) {
//用不同顏色填充連接區(qū)域中的每個(gè)黑色點(diǎn)
//floodFill就是把一個(gè)點(diǎn)x的所有相鄰的點(diǎn)都涂上x(chóng)點(diǎn)的顏色,一直填充下去购岗,直到這個(gè)區(qū)域內(nèi)所有的點(diǎn)都被填充完為止
Imgproc.floodFill(mat, new Mat(), new Point(i, j), new Scalar(color));
color++;
}
}
}
//統(tǒng)計(jì)不同顏色點(diǎn)的個(gè)數(shù)
int[] ColorCount = new int[255];
for (i = 0; i < nWidth; ++i) {
for (j = 0; j < nHeight; ++j) {
if (getPixel(j, i) != 255) {
ColorCount[getPixel(j, i) - 1]++;
}
}
}
//去除噪點(diǎn)
for (i = 0; i < nWidth; ++i) {
for (j = 0; j < nHeight; ++j) {
if (ColorCount[getPixel(j, i) - 1] <= pArea) {
setPixel(j, i, WHITE);
}
}
}
for (i = 0; i < nWidth; ++i) {
for (j = 0; j < nHeight; ++j) {
if (getPixel(j, i) < WHITE) {
setPixel(j, i, BLACK);
}
}
}
}
注:
本文章參考了很多博客汰聋,感謝;主要是跟著一個(gè)博客來(lái)實(shí)現(xiàn)的https://blog.csdn.net/ysc6688/article/category/2913009(也是基于opencv來(lái)做的喊积,只不過(guò)他是用c++實(shí)現(xiàn)的)感謝