分水岭算法
基本概念
1.任何一副灰度图像都可以被看成拓扑平面,灰度值高的区域可以被看成是山峰,灰度值低的区域可以被看成是山谷。我们向每一个山谷中灌不同颜色的水。随着水的位的升高,不同山谷的水就会相遇汇合,为了防止不同山谷的水汇合,我们需要在水汇合的地方构建起堤坝。不停的灌水,不停的构建堤坝知直到所有的山峰都被水淹没,我们构建好的堤坝就是对图像的分割。
2.在真实图像中,由于噪声点或者其它干扰因素的存在,使用分水岭算法常常存在过度分割的现象,这是因为很多很小的局部极值点的存在,如下图:
3.为了解决过度分割的问题,可以使用基于标记(mark)图像的分水岭算法,就是通过先验知识,来指导分水岭算法,以便获得更好的图像分段效果。通常的mark图像,都是在某个区域定义了一些灰度层级,在这个区域的洪水淹没过程中,水平面都是从定义的高度开始的,这样可以避免一些很小的噪声极值区域的分割
函数原型–watershed()
void watershed( InputArray image, InputOutputArray markers );
- image: 输入图像, 需为8位三通道彩色图像
- markers: 参数调用后的结果, 输入/输出32位单通道图像标记结果, 需和原图一样的大小
应用实例
代码
///分水岭算法
Mat srcImg = imread("bird.jpg");
imshow("src", srcImg);
Mat dstImg = srcImg.clone();
//medianBlur(srcImg, srcImg, 5);
//GaussianBlur(srcImg, srcImg, Size(5, 5), 0, 0);
Mat imgMask(srcImg.size(), CV_8U, Scalar(0));
//标示背景图像
rectangle(imgMask, Point(1,1), Point(srcImg.cols-2, srcImg.rows-2), Scalar(255), 1);
//标示鸟
rectangle(imgMask, Point(srcImg.cols/2-10, srcImg.rows/2-10),Point(srcImg.cols/2+10,srcImg.rows/2+10), cv::Scalar(128), 10);
//标示岩石
rectangle(imgMask, Point(264, 353),Point(314, 395), Scalar(64), 10);
imshow("mask", imgMask);
imgMask.convertTo(imgMask, CV_32S);
watershed(srcImg, imgMask); //调用分水岭算法
Mat mark1, mark2;
imgMask.convertTo(mark1, CV_8U);
imshow("marker", mark1);
Mat mark3 = mark1.clone();
bitwise_not(mark1, mark2);
threshold(mark1, mark1, 64, 255, CV_THRESH_TOZERO_INV);
threshold(mark2, mark2, 127, 255, CV_THRESH_TOZERO_INV);
threshold(mark3, mark3, 128, 255, CV_THRESH_BINARY);
vector<vector<Point>> contours;
vector<Vec4i> hierarcy;
findContours(mark1, contours, hierarcy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
drawContours(dstImg, contours, -1, Scalar(0, 255, 0), -1, 8);
vector<vector<Point>> contours2;
vector<Vec4i> hierarcy2;
findContours(mark2, contours2, hierarcy2, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
drawContours(dstImg, contours2, -1, Scalar(0, 0, 255), -1, 8);
vector<vector<Point>> contours3;
vector<Vec4i> hierarcy3;
findContours(mark3, contours3, hierarcy3, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
drawContours(dstImg, contours3, -1, Scalar(255, 0, 0), -1, 8);
Mat result = srcImg*0.5 + dstImg*0.5;
imshow("result", result);
waitKey(0);
图像修补
需添加opencv_photo248d.lib与photo.hpp
基本概念
OpenCV中图像修补技术由inpaint函数实现, 基本步骤是先修复区域边缘再逐步向内推进修复, 可以用来清除照片灰尘、划痕或者从静态图像及视频中去除不需要的物体。
函数原型
void inpaint( InputArray src, InputArray inpaintMask,
OutputArray dst, double inpaintRadius, int flags );
src: 输入图像, 需为8位单通道或三通道色图像
inpaintMask: 修复掩膜, 为八位单通道图像, 其中非0像素表示需要修补的区域
dst: 函数调用后输出图像, 和原图一样的尺寸和类型
inpaintRadius: 需要修补的每个点的圆形邻域, 为修复算法的参考半径
flags: 修补方法的标识符, 有如下两种:
基于Navier-Stokes方程方法和Alexandru Telea
应用实例1—景物修复
代码
///图像修补
Mat srcImg = imread("D:\\1\\snow.jpg");
Mat mask(srcImg.size(), CV_8U, Scalar(0));
rectangle(mask, Point(45, 270),Point(180, srcImg.rows), Scalar(255), -1, 8);
Point org = Point(185, 335);
putText( srcImg, "OpenCV", org, CV_FONT_HERSHEY_SIMPLEX, 2.2f, CV_RGB(255, 0, 0),2);
imshow("src", srcImg);
//putText( mask, "OpenCV", org, CV_FONT_HERSHEY_SIMPLEX, 2.2f, CV_RGB(255,255,255),2);
rectangle(mask, Point(185, 270),Point(srcImg.cols, srcImg.rows), Scalar(255), -1, 8);
imshow("mask", mask);
Mat result;
inpaint(srcImg, mask, result, 3, CV_INPAINT_NS); //图像修复
imshow("result", result);
waitKey(0);
运行结果
应用实例2—人脸去痘
思路:只需要在原图的复制图上绘制白色的线条作为掩码图