吐槽一下,在网上查了半天opencv3关于删除最小连通区域的方法,结果还是没找到,就自己写了一个,效果还可以,就发出来和大家分享一下。
思路:
1、遍历所有像素点
2、每遍历到一个黑点就去判断是否为连通区域起始点,若是则添加到待检测像素点集合中
3、遍历待检测像素点集合,检测待检测像素点周围(上下左右)的像素点,若是黑点,则添加到待检测像素点集合中,并把当前待检测像素点添加到已检测点集合。若待检测点集合中像素点数小于某个阈值时,就认为这些待检测点区域为最小连通区域。
4、查找到最小连通区域后把已检测点集合中的像素点设置成白点。
代码:
/**
* 判断是否为连通区域起始点(有点问题,有待优化)
* @param i 待检验点的横坐标
* @param j 待检验点的纵坐标
* @param srcImage 源图像
* @return true表示是连通区域起始点,false则反之
*/
public boolean isStart(int i, int j, Mat srcImage){
int before = j - 1;
int top = i - 1;
if((before<0||srcImage.get(i, before)[0] == 255.0) && (top<0 || srcImage.get(top, j)[0] == 255.0)){
return true;
}
return false;
}
public Mat deleteMinConnectedArea(Mat srcImage,double minarea){
int iw = srcImage.width(); //横向
int ih = srcImage.height(); //纵向
System.out.println("宽和高:"+ iw +" "+ ih);
for (int i = 0; i < ih; i++) {
for (int j = 0; j < iw; j++) {
double[] colors = srcImage.get(i, j);
if(colors[0] < 255.0 && isStart(i, j, srcImage)){
List<Point> checkPoint = new ArrayList<Point>();
List<Point> checkedPoint = new ArrayList<Point>();
checkPoint.add(new Point(i, j));
ergodic(srcImage, checkPoint, checkedPoint, (int) minarea);
}
}
}
return srcImage;
}
/**
* 连通像素计数
* @param i 第一个检测到的像素为0的横坐标
* @param j 第一个检测到的像素为0的纵坐标
* @param iw 源图像像素列数
* @param ih 源图像像素行数
* @param srcImage 源图像
* @param sp 待检测像素点
* @param lp 已检测像素点
*/
public void ergodic(Mat srcImage, List<Point> sp, List<Point> lp, int minArea ){
for (int k = 0; k < sp.size(); k++) {
check((int) sp.get(k).x, (int) sp.get(k).y, sp, lp, srcImage);
if(sp.size() >= minArea){
lp.removeAll(lp);
break;
}
}
if(lp.size() < minArea){
for (Point point : lp) {
srcImage.put((int) point.x, (int) point.y, 255.0);
}
}
}
/**
* 像素点判断是否已检测过
* @param i 待检验是否已检测像素点横坐标
* @param j 待检验是否已检测像素点纵坐标
* @param lp 已检测点集合
* @return 检测结果 false表示未检测,true表示已检测
*/
public boolean isExist(int i, int j, List<Point> lp){
if(lp.size() == 0){
return false;
}
for (Point point : lp) {
if(point.x == i && point.y == j ){
return true;
}
}
return false;
}
/**
* 像素点判断是否已被发现(存储在待检测集合)
* @param i 待检验是否已检测像素点横坐标
* @param j 待检验是否已检测像素点纵坐标
* @param sp 已检测点集合
* @return 检测结果 false表示未检测,true表示已检测
*/
public boolean isFind(int i, int j, List<Point> sp){
if(sp.size() == 0){
return false;
}
for (Point point : sp) {
if(point.x == i && point.y == j){
return true;
}
}
return false;
}
/**
* 检测当前像素点上下左右的像素,并把当前像素点添加到已检测集合
* @param i 检测当前像素点横坐标
* @param j 检测当前像素点纵坐标
* @param sp 待检测点集合
* @param lp 已检测点集合
* @param srcImage 源图像
*/
public void check(int i, int j, List<Point> sp, List<Point> lp, Mat srcImage){
int before = j - 1;
int after = j + 1;
int top = i - 1;
int bottom = i + 1;
if(before >= 0 && srcImage.get(i, before)[0] == 0.0){
if(!isExist(i, before, lp) && !isFind(i, before, sp)){
sp.add(new Point(i, before));
}
}
if(after < srcImage.width() && srcImage.get(i, after)[0] == 0.0){
if(!isExist(i, after, lp) && !isFind(i, after, sp)){
sp.add(new Point(i, after));
}
}
if(top >= 0 && srcImage.get(top, j)[0] == 0.0){
if(!isExist(top, j, lp) && !isFind(top, j, sp)){
sp.add(new Point(top, j));
}
}
if(bottom < srcImage.height() && srcImage.get(bottom, j)[0] == 0.0){
if(!isExist(bottom, j, lp) && !isFind(bottom, j, sp)){
sp.add(new Point(bottom, j));
}
}
lp.add(new Point(i,j));
}
public static void main(String[] args) {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Mat mat = Imgcodecs.imread("F:\\20170801161034.png");
Imgproc.cvtColor(mat, mat, Imgproc.COLOR_BGR2GRAY);
Imgproc.adaptiveThreshold(mat, mat, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 9, 0);
Mat dst = new DeleteMinArea().deleteMinConnectedArea(mat,50);
Imgcodecs.imwrite("F:\\31.jpg", dst);
}
源图片:
结果图片: