按面积选择区域 select_shape
二值化为了减少噪声的干扰,删除面积小的区域,代码中将连通区域面积(像素个数)不足100的区域认为是噪声点,并将其删除(即置为背景黑色)。
#include "stdafx.h"
#include <iostream>
#include<vector>
#include<algorithm>
#include <opencv2\opencv.hpp>
#include <opencv2\highgui\highgui.hpp>
using namespace std;
using namespace cv;
//轮廓按照面积大小升序排序
bool ascendSort(vector<Point> a, vector<Point> b)
{
return a.size() < b.size();
}
//轮廓按照面积大小降序排序
bool descendSort(vector<Point> a, vector<Point> b)
{
return a.size() > b.size();
}
int main()
{
Mat srcImage = imread("D:\\OpencvTest\\123.jpg");
Mat thresholdImage;
Mat grayImage;
cvtColor(srcImage, grayImage, CV_BGR2GRAY);
threshold(grayImage, thresholdImage, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
//Mat resultImage;
//thresholdImage.copyTo(resultImage);
vector< vector< Point> > contours; //用于保存所有轮廓信息
vector< vector< Point> > contours2; //用于保存面积不足100的轮廓
vector<Point> tempV; //暂存的轮廓
findContours(thresholdImage, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
//cv::Mat labels;
//int N = connectedComponents(resultImage, labels, 8, CV_16U);
//findContours(labels, contours2, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
//轮廓按照面积大小进行升序排序
sort(contours.begin(), contours.end(), ascendSort);//升序排序
vector<vector<Point> >::iterator itc = contours.begin();
int i = 0;
while (itc != contours.end())
{
//获得轮廓的矩形边界
Rect rect = boundingRect(*itc);
int x = rect.x;
int y = rect.y;
int w = rect.width;
int h = rect.height;
//绘制轮廓的矩形边界
cv::rectangle(srcImage, rect, { 0, 0, 255 }, 1);
//保存图片
char str[10];
sprintf(str, "%d.jpg", i++);
cv::imshow("srcImage", srcImage);
imwrite(str, srcImage);
waitKey(1000);
if (itc->size() < 100)
{
//把轮廓面积不足100的区域,放到容器contours2中,
tempV.push_back(Point(x, y));
tempV.push_back(Point(x, y+h));
tempV.push_back(Point(x+w, y+h));
tempV.push_back(Point(x+w, y));
contours2.push_back(tempV);
/*也可以直接用:contours2.push_back(*itc);代替上面的5条语句*/
//contours2.push_back(*itc);
//删除轮廓面积不足100的区域,即用黑色填充轮廓面积不足100的区域:
cv::drawContours(srcImage, contours2, -1, Scalar(0,0,0), CV_FILLED);
}
//保存图片
sprintf(str, "%d.jpg", i++);
cv::imshow("srcImage", srcImage);
imwrite(str, srcImage);
cv::waitKey(100);
tempV.clear();
++itc;
}
return 0;
}
填充空洞 fill_up
有的时候,我们需要对图像进行孔洞填充,一般想到的方法都是,使用findcontours函数,查找最外层轮廓,然后画出找到的轮廓。
这确实是一种方法,但似乎不够高效。下面贴出一个函数,可以更好的实现这个功能。
void fillHole(const cv::Mat srcimage, cv::Mat &dstimage)
{
Size m_Size = srcimage.size();
Mat tempImage = Mat::zeros(m_Size.height + 2, m_Size.width + 2, srcimage.type());//延展图像
srcimage.copyTo(temimage(Range(1, m_Size.height + 1), Range(1, m_Size.width + 1)));
floodFill(temimage, Point(0, 0), Scalar(255));
Mat cutImg;//裁剪延展的图像
tempImage(Range(1, m_Size.height + 1), Range(1, m_Size.width + 1)).copyTo(cutImg);
dstimage = srcimage | (~cutImg);
}
排序 sort_contour
按照x、y、面积排序
/**
* @brief 排序
* @param inputContours 输入轮廓
* @param outputContours 输出轮廓
* @param sortType 排序类型:0-按x排序,1-按y排序,2-按面积排序
* @param sortOrder 排序方式:true-升序,false-降序
*
* @return 返回说明
* -<em>false</em> fail
* -<em>true</em> succeed
*/
void CTestCvDlg::SortContourPoint(vector<vector<Point>> inputContours, vector<vector<Point>> &outputContours, int sortType, bool sortOrder)
{
vector<Point> tempContoursPoint;
//计算轮廓矩
vector<Moments> mu(inputContours.size());
//计算轮廓的质心
vector<Point2f> mc(inputContours.size());
//计算轮廓的面积
double area, area1;
//2.2 定义Rect类型的vector容器roRect存放最小外接矩形,初始化大小为contours.size()即轮廓个数
vector<RotatedRect> roRect(inputContours.size());
//计算轮廓最小外接矩形中心
Point center, center1;
for (int i = 0; i < inputContours.size(); i++)
{
tempContoursPoint.clear(); //每次循环注意清空
mu[i] = moments(inputContours[i], false); //轮廓矩
mc[i] = Point2d(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00); //轮廓质心
area = inputContours[i].size(); //轮廓点数
area = contourArea(inputContours[i], false); //轮廓的面积
//旋转矩形主要成员有center、size、 angle、points()
roRect[i] = minAreaRect(Mat(inputContours[i]));
center = roRect[i].center; //轮廓最小外接矩形中心
//outputContours[0] = inputContours[1];
}
//冒泡法
for (int i = 0; i < inputContours.size() - 1; i++) //n个数要进行n-1趟比较
{
for (int j = 0; j < (inputContours.size() - 1) - i; j++) //每趟比较n-i次
{
mu[j] = moments(inputContours[j], false); //轮廓矩
mu[j+1] = moments(inputContours[j+1], false); //轮廓矩
mc[j] = Point2d(mu[j].m10 / mu[j].m00, mu[j].m01 / mu[j].m00); //轮廓质心
mc[j+1] = Point2d(mu[j+1].m10 / mu[j+1].m00, mu[j+1].m01 / mu[j+1].m00); //轮廓质心
area = inputContours[j].size(); //轮廓点数
area = contourArea(inputContours[j], false); //轮廓的面积
area1 = inputContours[j+1].size(); //轮廓点数
area1 = contourArea(inputContours[j+1], false); //轮廓的面积
//旋转矩形主要成员有center、size、 angle、points()
roRect[j] = minAreaRect(Mat(inputContours[j]));
center = roRect[j].center; //轮廓最小外接矩形中心
roRect[j+1] = minAreaRect(Mat(inputContours[j+1]));
center1 = roRect[j+1].center; //轮廓最小外接矩形中心
if (mc[j].x > mc[j + 1].x) //依次比较两个相邻的数,将小数放在前面,大数放在后面:升序
{
tempContoursPoint = inputContours[j]; //temp是局部变量
inputContours[j] = inputContours[j + 1];
inputContours[j + 1] = tempContoursPoint;
}
}
}
outputContours = inputContours;
//for (int i = 0; i < inputContours.size(); i++)
//{
// tempContoursPoint.clear(); //每次循环注意清空
// mu[i] = moments(inputContours[i], false); //轮廓矩
// mc[i] = Point2d(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00); //轮廓质心
// area = inputContours[i].size(); //轮廓点数
// area = contourArea(inputContours[i], false); //轮廓的面积
// //旋转矩形主要成员有center、size、 angle、points()
// roRect[i] = minAreaRect(Mat(inputContours[i]));
// center = roRect[i].center; //轮廓最小外接矩形中心
// //outputContours[0] = inputContours[1];
//}
}
排序前:
排序后: