图像连通区域
图像的连通域是指图像中具有相同像素值并且位置相邻的像素组成的区域,连通域分析是指在图像中寻找出彼此互相独立的连通域并将其标记出来。提取图像中不同的连通域是图像处理中较为常用的方法,例如在车牌识别、文字识别、目标检测等领域对感兴趣区域分割与识别。一般情况下,一个连通域内只包含一个像素值,因此为了防止像素值波动对提取不同连通域的影响,连通域分析常处理的是二值化后的图像。
邻域
邻域即相邻得区域,opencv中有以下两种形式得领域
- 4-邻域:必须在水平和垂直方向上相邻,相邻的两个像素坐标必须只有一位不同而且只能相差1个像素
- 8-邻域: 九宫格形式,相邻的两个像素坐标必须只有一位不同而且只能相差1个像素
图像邻域分析法
两遍扫描法
两遍扫描法会遍历两次图像,第一次遍历图像时会给每一个非0像素赋予一个数字标签,当某个像素的上方和左侧邻域内的像素已经有数字标签时,取两者中的最小值作为当前像素的标签,否则赋予当前像素一个新的数字标签。第一次遍历图像的时候同一个连通域可能会被赋予一个或者多个不同的标签
种子填充法
首先将所有非0像素放到一个集合中,之后在集合中随机选出一个像素作为种子像素,根据邻域关系不断扩充种子像素所在的连通域,并在集合中删除掉扩充出的像素,直到种子像素所在的连通域无法扩充,之后再从集合中随机选取一个像素作为新的种子像素,重复上述过程直到集合中没有像素(类似DFS)
连通区域操作
不带统计信息的API
int connectedComponents(InputArray image, OutputArray labels,int connectivity = 8, int ltype = CV_32S);
/*******************************************************************
* image: 输入二值图像
* labels: 输出图像
* connectivity: 邻域
* ltype: 输出图深度
*********************************************************************/
带有统计信息的API
int connectedComponentsWithStats(InputArray image, OutputArray labels, OutputArray stats, OutputArray centroids,int connectivity, int ltype, int ccltype);
/*******************************************************************
* image: 输入二值图像
* labels: 输出图像
* stats: 统计信息,包括每个组件的位置、宽、高与面积
* centroids: 每个组件的中心位置坐标cx, cy
* connectivity: 邻域
* ltype: 输出图深度
* ccltype: 连通组件算法
*********************************************************************/
//ccltype取值
enum ConnectedComponentsTypes {
CC_STAT_LEFT = 0, // 组件的左上角点像素点坐标的X位置
CC_STAT_TOP = 1, // 组件的左上角点像素点坐标的Y位置
CC_STAT_WIDTH = 2, // 组件外接矩形的宽度
CC_STAT_HEIGHT = 3, // 组件外接矩形的高度
CC_STAT_AREA = 4, // 当前连通组件的面积(像素单位)
CC_STAT_MAX = 5 // 最大枚举值,仅在内部用于内存分配(可忽略)
};
综合代码
#include <iostream>
#include <string>
#include <time.h>
#include <map>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
class Connection
{
public:
Connection() :img(imread("text.jpg",IMREAD_GRAYSCALE))
{
result["原图"] = img;
threshold(img, result["阈值化"], 200, 255, THRESH_BINARY_INV);
}
//着色
void DrawColor(Mat& src, Mat& result, int nCount)
{
vector<Vec3b> colors(nCount);
for (int i = 1; i < nCount; i++)
{
colors[i] = Vec3b(rand() % 256, rand() % 256, rand() % 256);
}
//连通区域着色
result = Mat::zeros(img.size(), CV_8UC3);
for (int y = 0; y < img.rows; y++)
{
for (int x = 0; x < img.cols; x++)
{
int label = src.at<int>(y, x);
if (label > 0 && label <= nCount)
{
result.at<Vec3b>(y, x) = colors[label];
}
if (label == 0)
{
result.at<Vec3b>(y, x) = Vec3b(255, 255, 255);
}
}
}
}
void NoCount()
{
Mat temp;
int nCount = connectedComponents(result["阈值化"], temp);
//temp不能直接显示,需要转换后才能显示
DrawColor(temp, result["不统计"], nCount);
}
void Count()
{
Mat stats, center, temp;
int nCount = connectedComponentsWithStats(result["阈值化"], temp, stats, center, 8, CC_STAT_AREA);
DrawColor(temp, result["统计"], nCount);
//利用统计信息标记连通域
for (int i = 1; i < nCount; i++)
{
int x = stats.at<int>(i, CC_STAT_LEFT);
int y = stats.at<int>(i, CC_STAT_TOP);
int w = stats.at<int>(i, CC_STAT_WIDTH);
int h = stats.at<int>(i, CC_STAT_HEIGHT);
rectangle(result["统计"], Rect(x, y, w, h), Scalar(0, 0, 0), 2);
}
}
void Show()
{
for (auto& v : result)
{
imshow(v.first, v.second);
}
waitKey(0);
}
protected:
Mat img;
map<string, Mat> result;
};
int main()
{
srand((unsigned int)time(nullptr));
Connection* p = new Connection;
p->NoCount();
p->Count();
p->Show();
return 0;
}
OpenCV图像轮廓
图像轮廓
图像轮廓是一系列相连的点组成的曲线,代表了物体的基本外形,相对于边缘,轮廓是连续的,边缘并不全部连续。一般地,获取图像轮廓要经过下面几个步骤:
- 读取图片
- 灰度处理
- 二值化处理,查找轮廓
- 显示轮廓边缘
查找轮廓
void findContours( InputArray image, OutputArrayOfArrays contours,int mode, int method, Point offset = Point());
/*******************************************************************
* image: 单通道灰度图
* contours: 检测到的轮廓,存储的是点坐标
* mode: 轮廓检索模式
* method: 轮廓逼近方法
* offset: 按照偏移量移动所有的轮廓 一般不使用
*********************************************************************/
void findContours( InputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode,int method, Point offset = Point());
/*******************************************************************
* image: 输入单通道灰度图
* contours: 检测到的轮廓,存储的是点坐标
* hierarchy: 轮廓的拓扑信息
* contours[i]包含4个hierarchy[i]元素,
* hierarchy[i][0]:后一个轮廓
* hierarchy[i][1]:前一个轮廓
* hierarchy[i][2]:父轮廓
* hierarchy[i][3]:内嵌轮廓
* mode: 轮廓检索模式
* method: 轮廓逼近方法
* offset: 按照偏移量移动所有的轮廓 一般不使用
*********************************************************************/
//mode取值:
enum RetrievalModes {
RETR_EXTERNAL = 0,//只检索最外面的轮廓
RETR_LIST = 1,//检索所有轮廓,并保存到一条链表中
RETR_CCOMP = 2,//检索所有轮廓,组织为顶层(外部边界)和次层(内部边界)
RETR_TREE = 3,//检索所有的轮廓,并重构嵌套轮廓的整个层次
RETR_FLOODFILL = 4 //支持CV_32SC1图像,不等于仅支持CV_8UC1图像
};
//method取值:
enum ContourApproximationModes {
CHAIN_APPROX_NONE = 1, //保存所有轮廓点
CHAIN_APPROX_SIMPLE = 2, //压缩水垂直对角线,只保留线的两端点
CHAIN_APPROX_TC89_L1 = 3, //使用teh-Chinl chain 近似算法
CHAIN_APPROX_TC89_KCOS = 4 //使用teh-Chinl chain 近似算法
};
绘制轮廓
void drawContours( InputOutputArray image, InputArrayOfArrays contours,int contourIdx, const Scalar& color,
int thickness = 1, int lineType = LINE_8,InputArray hierarchy = noArray(),int maxLevel = INT_MAX, Point offset = Point());
/*******************************************************************
* image: 输出单通道灰度图
* contours: 所有的输入轮廓
* contourIdx: 指定轮廓列表的索引 ID(将被绘制)
* 若为负数,则所有的轮廓将会被绘制
* color: 绘制轮廓的颜色
* thickness: 绘制轮廓线条的宽度
* lineType: 线条的类型,8连通型或4连通型
* hierarchy: 层次结构信息
* maxLevel: 绘制轮廓的最高级别
* 0:绘制指定轮廓
* 1:绘制该轮廓和所有嵌套轮廓
* 2:绘制该轮廓、嵌套轮廓子轮廓等
* offset: 按照偏移量移动所有的轮廓
*********************************************************************/
综合代码
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
class OutLine
{
public:
OutLine() :img(imread("testMin.jpg", IMREAD_GRAYSCALE))
{
result["Gray"] = img;
}
void Show()
{
for (auto& v : result)
{
imshow(v.first, v.second);
}
waitKey(0);
}
//查找轮廓
void FindOutLine()
{
threshold(result["Gray"], result["Binary"], 125, 255, THRESH_BINARY);
findContours(result["Binary"], contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
}
//着色
void DrawOutLine()
{
result["OutLine"] = Mat::zeros(result["Binary"].size(), CV_8UC3);
for (int i = 0; i < contours.size(); i++)
{
Scalar color = Scalar(0, 255, 0);
//drawContours(result["OutLine"], contours, i, color, 1, 8, hierarchy);
drawContours(result["OutLine"], contours, i, color);
}
}
private:
Mat img;
map<string, Mat> result;
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
};
int main()
{
OutLine* p = new OutLine;
p->FindOutLine();
p->DrawOutLine();
p->Show();
return 0;
}