一、积分图像
积分图像是对原图像进行积分操作的算法。如上图左所示,不同颜色代表不同区域。当我们想求取一个像素点的积分值时,我们需要求取该点左上方区域的数据之和,如P0的积分值是浅蓝色区域的数据之和。 P1的积分值为蓝色和橙色区域的数值之和。
上图展示的是倾斜求和积分方式。
积分图像计算函数:
integral(InputArray src, OutputArray sum, OutputArray sqsum, OutputArray tilted, int sdepth = -1, int sqdepth = -1)
src:输入图像,图像数据类型可以是CV_8U、CV_32F或者CV_64F。
sum:输出标准求和积分图像,图像的数据类型可以是CV_32S、CV_32F或者CV_64F。
sqsum:输出平方求和积分图像,图像的数据类型可以是CV_32F或者CV_64F。
tilted:输出倾斜45°的倾斜求和积分图像,其数据类型与sum相同。
sdepth:输出标准求和积分图像和倾斜求和积分图像的数据类型标志,可以选择的参数为CV_32S,CV_32F或者CV_64F,参数默认值为-1,表示满足数据存储的自适应类型。
sqdepth:输出平方求和积分图像的数据类型标志,可以选择的参数为CV_32F或者CV_64F,参数默认值为-1,表示满足数据存储的自适应类型。
本节使用案例如下:
int main() {
//创建16*16的全1矩阵
Mat img = Mat::ones(16, 16, CV_32FC1);
//在图像中加入随机噪声
RNG rng(10000);
for (int y = 0; y < img.rows; y++)
{
for (int x = 0; x < img.cols; x++)
{
float d = rng.uniform(-0.5, 0.5);
img.at<float>(y, x) = img.at<float>(y, x) + d;
}
}
//计算标准求和积分
Mat sum;
integral(img, sum);
//为了便于显示,转成CV_8U格式
Mat sum8U = Mat_<uchar>(sum);
namedWindow("q", WINDOW_NORMAL);
imshow("q", sum8U);
//计算平方求和积分
Mat sqsum;
integral(img, sum, sqsum);
//为了便于显示,转成CV_8U格式
Mat sqsum8U = Mat_<uchar>(sqsum);
namedWindow("w", WINDOW_NORMAL);
imshow("w", sqsum8U);
//计算倾斜求和积分
Mat tilted;
integral(img, sum, sqsum, tilted);
//为了便于显示,转成CV_8U格式
Mat tilted8U = Mat_<uchar>(tilted);
namedWindow("e", WINDOW_NORMAL);
imshow("e", tilted8U);
waitKey(0);
return 0;
}
二、图像分割——漫水填充
floodFill(InputOutputArray image, InputOutputArray mask, Point seedPoint, Scalar newVal, Rect * rect = 0, Scalar loDiff = Scalar(), upDiff =, Scalar scalar(), int flags = 4
image:输入输出图像,图像数据类型可以为CV_8U或者CV_32F的单通道或者三通道图像。
mask:掩码矩阵,尺寸比如输入图像的宽和高各大2的单通道图像,用于标记漫水填充的区域。
seedPoint:种子点。
newVal:归入种子点区域内像素点的新像素值。
rect:种子点漫水填充区域的最小矩形边界,默认值为0,表示不输出边界。loDiff:添加进种子点区域条件的下界差值。
upDiff:添加进种子点区域条件的上界差值。
flags:漫水填充方法的操作标志。
本节应用案例如下:
int main() {
//读取图片
Mat src = imread("1.png");
if (src.empty())
{
printf("不能打开空图片");
return -1;
}
//在生成随机像素的随机数
RNG rng(10000);
//连同邻域方式
int connectivity = 4;
//掩码图像数值
int maskVal = 255;
//漫水填充的标志位,不改变种子像素值
int flags = connectivity | (maskVal << 8) | FLOODFILL_FIXED_RANGE;
//设置与选中像素点的差值,上下都为20
Scalar loDiff = Scalar(20, 20, 20);
Scalar upDiff = Scalar(20, 20, 20);
//声明掩膜矩阵变量
Mat mask = Mat::zeros(src.rows + 2, src.cols + 2, CV_8UC1);
while (true)
{
//随机产生图像中某一像素点
int py = rng.uniform(0, src.rows - 1);
int px = rng.uniform(0, src.cols - 1);
Point point = Point(px, py);
//对彩色图像填充像素值
Scalar newVal = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
//进行漫水填充,返回填充像素数目
int area = floodFill(src, mask, point, newVal, &Rect(), loDiff, upDiff, flags);
imshow("q", mask);
//按ESC键退出程序
char key = (char)waitKey();
if (key == 27 || key == 'q' || key == 'Q')
{
break;
}
}
waitKey(0);
return 0;
}
三、图像分割方法——分水岭法
watershed(InputArray image, InputOutputArray markers)
image:输入图像,数据类型为CV_8U的三通道图像。
markers:输入(种子区域)/输出CV_32S的单通道图像的标记结果,与原图像具有相同的尺寸。
本节样例如下:
int main() {
//读取要标记的图片
Mat src = imread("1.png");
//读取原始图片
Mat src1 = imread("3.png");
if (src.empty() || src1.empty())
{
printf("不能打开空图片");
return -1;
}
//watershed参数设置
Mat maskWaterShed;
//转化为灰度图
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
Mat imgMask;
//二值化并开运算
threshold(gray, imgMask, 254, 255, THRESH_BINARY);
Mat k = getStructuringElement(0, Size(3, 3));
morphologyEx(imgMask, imgMask, MORPH_OPEN, k);
//轮廓提取
vector<vector<Point>>contours;
vector<Vec4i> hierarchy;
findContours(imgMask, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
//在maskWaterShed上绘制轮廓,用于输入分水岭算法
maskWaterShed = Mat::zeros(imgMask.size(), CV_32S);
for (int index = 0; index < contours.size(); index++)
{
drawContours(maskWaterShed, contours, index, Scalar::all(index + 1), -1, 8, hierarchy, INT_MAX);
}
//进行分水岭算法
watershed(src1, maskWaterShed);
//随机生成颜色,用于显示分割
vector<Vec3b> colors;
for (int i = 0; i < contours.size(); i++)
{
int b = theRNG().uniform(0, 255);
int g = theRNG().uniform(0, 255);
int r = theRNG().uniform(0, 255);
colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
}
//显示图像
Mat resultImg = Mat(src1.size(), CV_8UC3);
for (int i = 0; i < imgMask.rows; i++)
{
for (int j = 0; j < imgMask.cols; j++)
{
//绘制不同区域
int index = maskWaterShed.at<int>(i, j);
//如果区域边界
if (index == -1)
{
resultImg.at<Vec3b>(i, j) = Vec3b(255, 255, 255);
}
//没有被标记的区域置0
else if (index<=0 || index > contours.size())
{
resultImg.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
}
//其他区域保持像素不变
else
{
resultImg.at<Vec3b>(i, j) = colors[index - 1];
}
}
}
imshow("resultImg", resultImg);
resultImg = resultImg * 0.8 + src1 * 0.2;
imshow("resultImg1", resultImg);
//绘制每个区域的图案
for (int n = 0; n < contours.size(); n++)
{
Mat resImage1 = Mat(src.size(), CV_8UC3);
for (int i = 0; i < imgMask.rows; i++)
{
for (int j = 0; j < imgMask.cols; j++)
{
//绘制不同区域
int index = maskWaterShed.at<int>(i, j);
//如果区域边界
if (index == n)
{
resultImg.at<Vec3b>(i, j) = src1.at<Vec3b>(i, j);
}
else
resultImg.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
}
}
imshow(to_string(n), resImage1);
}
waitKey(0);
return 0;
}