本文用于实战OpenCV3 imgproc模块的基本方法!!!
具体函数定义可参考OpenCV3 Library API参考网站,其中包括Python API和C++ API:
https://docs.opencv.org/3.0-beta/modules/refman.html
/* main.c */
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
#define WINDOW_NAME "window"
int g_nThresholdValue = 100;
int g_nThresholdType = 3;
Mat g_thresholdImg, g_grayImg;
static void on_Threshold(int, void*);
int main(int *argc, int **argv)
{
Mat srcImg = imread("lena.jpg");
//1.线性滤波操作
Mat boxFilterImg, blurImg, GaussianBlurImg;
//方框滤波
boxFilter(srcImg, boxFilterImg, -1, Size(5, 5));
imshow("boxFilter", boxFilterImg);
//均值滤波
blur(srcImg, blurImg, Size(5, 5));
imshow("blur", blurImg);
//高斯滤波
GaussianBlur(srcImg, GaussianBlurImg, Size(5, 5), 0, 0);
imshow("GassianBlur", GaussianBlurImg);
//2.非线性滤波
Mat medianBlurImg, bilateralFilterImg;
//中值滤波
medianBlur(srcImg, medianBlurImg, 5);
imshow("medianBlur", medianBlurImg);
//双边滤波
bilateralFilter(srcImg, bilateralFilterImg, 5, 5 * 2, 5 / 2);
imshow("bilateraFilter", bilateralFilterImg);
//3.形态学滤波
Mat dilateImg, erodeImg, morphologyExImg;
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
//膨胀操作
dilate(srcImg, dilateImg, element);
imshow("dilate", dilateImg);
//腐蚀操作
erode(srcImg, erodeImg, element);
imshow("erode", erodeImg);
//高级形态学操作,开运算、闭运算、形态学梯度、顶帽、黑帽(MORPH_BLACKHAT)
//通过设置不同的标识符选择不同的方法
morphologyEx(srcImg, morphologyExImg, MORPH_BLACKHAT, element);
imshow("MORPH_BLACKHAT", morphologyExImg);
//4.漫水填充
Rect rect;
Mat floodFillImg = srcImg.clone();
floodFill(floodFillImg, Point(100, 100), Scalar(10, 10, 10), &rect, Scalar(10, 10, 10), Scalar(10, 10, 10));
imshow("floodFill", floodFillImg);
//5.图像尺寸调整
Mat resizeImg1, resizeImg2;
resize(srcImg, resizeImg1, Size(srcImg.cols / 2, srcImg.rows / 2), 0, 0, INTER_LINEAR);
imshow("resize1", resizeImg1);
resize(srcImg, resizeImg2, Size(), 0.5, 0.5, INTER_AREA);
imshow("resize2", resizeImg2);
//6.图像金字塔
Mat pyrUpImg, pyrDownImg;
//向上采样
pyrUp(srcImg, pyrUpImg, Size(srcImg.cols * 2, srcImg.rows * 2));
imshow("pyrUp", pyrUpImg);
//向下采样
pyrDown(srcImg, pyrDownImg, Size(srcImg.cols / 2, srcImg.rows / 2));
imshow("pyrDown", pyrDownImg);
//7.固定阈值操作
cvtColor(srcImg, g_grayImg,COLOR_RGB2GRAY);
namedWindow(WINDOW_NAME, WINDOW_AUTOSIZE);//先创建窗口再创建滑动条
createTrackbar("model", WINDOW_NAME, &g_nThresholdType, 4, on_Threshold);
createTrackbar("threshold", WINDOW_NAME, &g_nThresholdValue, 255, on_Threshold);
on_Threshold(0, 0);
//8.Canny边缘检测
Mat grayImg, edgeImg, colorImg;
colorImg.create(srcImg.size(), srcImg.type());
colorImg = Scalar::all(0);
cvtColor(srcImg, grayImg, COLOR_RGB2GRAY);
blur(grayImg, edgeImg, Size(3, 3));
Canny(edgeImg, edgeImg, 3, 9, 3);
imshow("gray edge", edgeImg);
srcImg.copyTo(colorImg, edgeImg);
imshow("color edge", colorImg);
//9.重映射,不改变像素值,仅移动像素位置
Mat map_x, map_y, remapImg;
map_x.create(srcImg.size(), CV_32FC1);
map_y.create(srcImg.size(), CV_32FC1);
for (int j = 0; j < srcImg.rows; ++j)
{
for (int i = 0; i < srcImg.cols; ++i)
{
map_x.at(j, i) = static_cast(map_x.cols - i);
map_y.at(j, i) = static_cast(map_y.rows - j);
}
}
remap(srcImg, remapImg, map_x, map_y, INTER_LINEAR);
imshow("remap", remapImg);
//10.仿射变换 仿射变换旋转
Mat warpImg, warpRotImg;
Point2f srcWarp[3],dstWarp[3];
srcWarp[0] = Point2f(0, 0);
srcWarp[1] = Point2f(static_cast(srcImg.cols - 1), 0);
srcWarp[2] = Point2f(0, static_cast(srcImg.rows - 1));
dstWarp[0] = Point2f(0.0, static_cast(srcImg.rows *0.4));
dstWarp[1] = Point2f(static_cast(srcImg.cols *0.8), static_cast(srcImg.rows *0.3));
dstWarp[2] = Point2f(static_cast(srcImg.cols *0.5), static_cast(srcImg.rows *0.6));
Mat warpMat(2,3,CV_32FC1);
//获得仿射矩阵
warpMat = getAffineTransform(srcWarp, dstWarp);
warpAffine(srcImg, warpImg, warpMat, srcImg.size());
imshow("warpImg", warpImg);
Mat warpRotMat(2, 3, CV_32FC1);
Point2f center=Point(warpImg.cols/2, warpImg.rows/2);
//获得旋转矩阵
warpRotMat = getRotationMatrix2D(center, 30, 0.5);
warpAffine(warpImg, warpRotImg, warpRotMat, srcImg.size());
imshow("warpRotImg", warpRotImg);
//11.直方图均衡化
Mat tempImg = srcImg.clone();
Mat equalizeImg;
vector channels;
split(tempImg, channels);
Mat blueImg = channels.at(0);
equalizeHist(blueImg, equalizeImg);//处理蓝色通道
imshow("equalizeHist", equalizeImg);
//12.查找并绘制轮廓
Mat grayImg, contoursImg;
contoursImg = Mat::zeros(srcImg.size(), CV_8UC3);
cvtColor(srcImg, grayImg, COLOR_RGB2GRAY);
grayImg = grayImg > 50;//灰度图二值化
vector> contours;
vector hierarchy;
findContours(grayImg, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);//寻找轮廓
int index = 0;
for (; index >= 0; index = hierarchy[index][0])//将下一个轮廓的索引编号赋值给index
{
Scalar color(255, 255, 255);
drawContours(contoursImg, contours, index, color, CV_FILLED, 8, hierarchy);
}
imshow("grayIage", grayImg);
imshow("contours", contoursImg);
//13.寻找凸包,绘制凸包
Mat whiteImg(512,512,CV_8UC3);
whiteImg = Scalar::all(0);
RNG &rng = theRNG();
int countRNG = (unsigned)rng % 100 + 3;
vector points;
for (int i = 0; i < countRNG; ++i)//生成随机数
{
Point point;
point.x = rng.uniform(whiteImg.cols/4, whiteImg.cols/4 * 3);
point.y = rng.uniform(whiteImg.rows / 4, whiteImg.rows / 4 * 3);
points.push_back(point);
}
Scalar color = Scalar::all(255);
for (int j = 0; j < countRNG; ++j)//绘制随机数
circle(whiteImg, points[j], 3, color, CV_FILLED, CV_AA);
vector hull;
convexHull(Mat(points), hull, true);//返回凸包点在点集中的索引
imshow("points", whiteImg);
int hullCount = (int)hull.size();
Point point0 = points[hull[hullCount - 1]];
for (int k = 0; k < hullCount; ++k)//连接各个凸包点
{
Point point = points[hull[k]];
line(whiteImg, point0, point, Scalar::all(255), 2, 8);
point0 = point;
}
imshow("hull", whiteImg);
//14.RGB转化为HSV色彩空间
Mat hsvImg;
cvtColor(srcImg, hsvImg, COLOR_RGB2HSV);
imshow("hsvImg", hsvImg);
while (1)
{
char c = waitKey(5);//获取按键
if ((char)c == 49)//按键为1,退出窗口
break;
}
return 0;
}
static void on_Threshold(int, void*)
{
threshold(g_grayImg, g_thresholdImg, g_nThresholdValue, 255, g_nThresholdType);
imshow(WINDOW_NAME, g_thresholdImg);
}
所用知识:
1.若以彩色模式载入图像,即通道顺序为RGB,解码后的图像以BGR的通道顺序进行存储;
2.waitKey()函数的功能是不断刷新图像,频率时间为delay,单位为ms,返回值为当前键盘按键值。delay>0时,延迟"delay"ms,在显示视频时这个函数是有用的,用于设置在显示完一帧图像后程序等待"delay"ms再显示下一帧视频;如果使用waitKey(0)则只会显示第一帧视频;
3. copyTo最一般的用法是src.copyTo(dst),将src复制到dst矩阵中;src.copyTo( dst,detected_edges); 是将src中detected_edges矩阵对应的非零部分(即边缘检测结果)复制到dst中。所以最终显示的边缘和原图颜色一样,也可以直接显示detected_edges矩阵(黑白)。
4.现实世界的三维空间映射到图像显示的二维空间中会丢失很多信息,也会添进来一部分类似光照、场景等的干扰。如何获得二维图像中的边缘?图像处理中认为,灰度值变化剧烈的地方就是边缘。那么如何判断灰度值变化?如何度量“剧烈”?sobel算子就对这些问题做了自己的规范,而且命名为sobel算子。
sobel算子的思想,Sobel算子认为,邻域的像素对当前像素产生的影响不是等价的,所以距离不同的像素具有不同的权值,对算子结果产生的影响也不同。一般来说,距离越远,产生的影响越小。
sobel算子的原理,对传进来的图像像素做卷积,卷积的实质是在求梯度值,或者说给了一个加权平均,其中权值就是所谓的卷积核;然后对生成的新像素灰度值做阈值运算,以此来确定边缘信息。
Sobel算子边缘检测过程:(1)滤波预处理,减少噪声对sobel算子的影响;(2) 对原图像素进行卷积获得新的像素(X方向上和Y方向上的梯度);(3) 给定阈值,大于阈值的为边缘,不大于的不为边缘;
5.膨胀、腐蚀、开、闭运算是数学形态学最基本的变换。
膨胀:求局部最大值(卷积),把二值图像各1像素连接成分的边界扩大一层(填充边缘或0像素内部的孔);
腐蚀:求局部最小值(卷积),把二值图像各1像素连接成分的边界点去掉从而缩小一层(可提取骨干信息,去掉毛刺,去掉孤立的0像素);开:先腐蚀再膨胀,可以去掉目标外的孤立点;闭:先膨胀再腐蚀,可以去掉目标内的孔。
6.漫水填充,简单来说,就是自动选中了和种子点相连的区域,接着将该区域替换成指定的颜色,这是个非常有用的功能,经常用来标记或者分离图像的一部分进行处理或分析.漫水填充也可以用来从输入图像获取掩码区域,掩码会加速处理过程,或者只处理掩码指定的像素点. 漫水填充不会填充掩膜mask的非零像素区域。例如,一个边缘检测算子的输出可以用来作为掩膜,以防止填充到边缘。同样的,也可以在多次的函数调用中使用同一个掩膜,以保证填充的区域不会重叠。另外需要注意的是,掩膜mask会比需填充的图像大,所以 mask 中与输入图像(x,y)像素点相对应的点的坐标为(x+1,y+1)。
在OpenCV 2.X中,使用C++重写过的FloodFill函数有两个版本。一个不带掩膜mask的版本,和一个带mask的版本。这个掩膜mask,就是用于进一步控制哪些区域将被填充颜色(比如说当对同一图像进行多次填充时)。
这两个版本的FloodFill,都必须在图像中选择一个种子点,然后把临近区域所有相似点填充上同样的颜色,不同的是,不一定将所有的邻近像素点都染上同一颜色,漫水填充操作的结果总是某个连续的区域。当邻近像素点位于给定的范围(从loDiff到upDiff)内或在原始seedPoint像素值范围内时,FloodFill函数就会为这个点涂上颜色。
参考资料:
1.毛星云等编著《OpenCV3编程入门》;
2.CSDN相关博客和其他网页资料,因为是随用随查,仅将关键内容存于笔记中,然后在此贴出,所以并未保存链接;