本节主要介绍OpenCV的imgproc模块的图像变换部分:
1. 边缘检测:canny算子,sobel算子,高斯拉普拉斯算子(LOG)
2. 霍夫变换
3. 重映射
4. 仿射变换
5. 直方图均衡化
景深不连续,反射率不连续(如表面材质的改变),光照不连续(阴影等),如下图
滤波(因为导数对噪声比较敏感,一般使用高斯滤波),增强边缘(即将图像像素点邻域强度值有显著变化的点凸显出来,这里可以通过计算梯度幅值来确定),检测(常通过阈值化方法来对这些点进行取舍,这里要用到非极大值抑制啦)。
我们知道高斯平滑是一个低通滤波器,而拉普拉斯则相当于一个高通滤波器,推导如下:
这个概念应用很广泛,在canny中也有应用,canny算法很经典,务必要搞清楚。
这里给一页slide说明为什么要使用它
原理最好自己推一遍,这里需要说明一下hough变换可用于检测任何能写出解析式的简单形状。这里说一下HoughLines()函数,它的输出(rho, theta),rho表示距离原点(图像左上角)的距离,theta表示以y轴反方向为参考的旋转角度。自己写了个demo尝试了一下,结果如下(几条线的交点坐标为(250, 250)):
即y=kx+b。摄像机从不同角度拍摄同一物体即是仿射变换,经过仿射变换原来在同一直线上的点仍然在同一直线上。
其中方法CLAHE可参见http://blog.csdn.net/nnnnnnnnnnnny/article/details/52681591
本章示例较多,示例列表:
1.边缘检测
2.霍夫变换
3.重映射
4.仿射变换
5.直方图均衡化
源码:
#include
#include
using namespace std;
using namespace cv;
//原图,原图的灰度图,目标图
Mat g_srcImage, g_srcGrayImage, g_dstImage;
//Canny边缘检测相关变量
Mat g_cannyDetectedEdges;
int g_cannyLowThreshold = 1; //TrackBar位置参数
//Sobel边缘检测相关变量
Mat g_sobelGradient_X, g_sobelGradient_Y;
Mat g_sobelAbsGradient_X, g_sobelAbsGradient_Y;
int g_sobelKernelSize = 1; //TrackBar位置参数
//全局函数声明
static void on_Canny(int, void*);
static void on_Sobel(int, void*);
void Scharr();
int main()
{
g_srcImage = imread("poster_girl_3.jpg");
namedWindow("【原始图】");
imshow("【原始图】", g_srcImage);
cvtColor(g_srcImage, g_srcGrayImage, COLOR_BGR2GRAY);
g_dstImage.create(g_srcImage.size(), g_srcImage.type());
namedWindow("【效果图】Canny边缘检测");
namedWindow("【效果图】Sobel边缘检测");
createTrackbar("参数值:", "【效果图】Canny边缘检测", &g_cannyLowThreshold, 120, on_Canny);
createTrackbar("参数值:", "【效果图】Sobel边缘检测", &g_sobelKernelSize, 3, on_Sobel);
on_Canny(0, 0);
on_Sobel(0, 0);
while (waitKey(8) != 27);
return 0;
}
void on_Canny(int, void*)
{
blur(g_srcGrayImage, g_cannyDetectedEdges, Size(3, 3)); //先使用3*3内核来降噪
Canny(g_cannyDetectedEdges, g_cannyDetectedEdges, g_cannyLowThreshold, g_cannyLowThreshold*3, 3);
g_dstImage = Scalar::all(0); //将g_dstImage内的所有元素设置为0
//使用Canny算子输出的边缘图g_cannyDetectedEdges作为掩码,来将原图g_srcImage拷到目标图g_dstImage中
g_srcImage.copyTo(g_dstImage, g_cannyDetectedEdges);
imshow("【效果图】Canny边缘检测", g_dstImage);
}
void on_Sobel(int, void*)
{
Sobel(g_srcImage, g_sobelGradient_X, CV_16S, 1, 0, (2*g_sobelKernelSize+1)); //求X方向梯度
convertScaleAbs(g_sobelGradient_X, g_sobelAbsGradient_X); //计算绝对值,并将结果转换成8位
Sobel(g_srcImage, g_sobelGradient_Y, CV_16S, 0, 1, (2 * g_sobelKernelSize + 1)); //求Y方向梯度
convertScaleAbs(g_sobelGradient_Y, g_sobelAbsGradient_Y); //计算绝对值,并将结果转换成8位
addWeighted(g_sobelAbsGradient_X, 0.5, g_sobelAbsGradient_Y, 0.5, 0, g_dstImage);
imshow("【效果图】Sobel边缘检测", g_dstImage);
}
源码:
#include
#include
using namespace cv;
using namespace std;
Mat g_srcImage, g_dstImage, g_midImage; //原始图、中间图和效果图
vector g_lines; //定义一个矢量结构g_lines用于存放得到的线段矢量几何
int g_nthreshold = 100;//变量接受的TrackBar位置参数
static void on_HoughLines(int, void*);
int main()
{
g_srcImage = imread("poster_building.jpg");
imshow("【原始图】", g_srcImage);
namedWindow("【效果图】");
createTrackbar("值", "【效果图】", &g_nthreshold, 200, on_HoughLines);
Canny(g_srcImage, g_midImage, 50, 200, 3); //进行一次canny边缘检测
cvtColor(g_midImage, g_dstImage, COLOR_GRAY2BGR); //转化边缘检测后的灰度图转化为三通道图
on_HoughLines(g_nthreshold, 0);
while (waitKey(1) != 27);
return 0;
}
static void on_HoughLines(int, void*)
{
Mat dstImage = g_dstImage.clone();
Mat midImage = g_midImage.clone();
vector mylines;
HoughLinesP(midImage, mylines, 1, CV_PI / 180, g_nthreshold+1, 50, 10);
//绘制每一条线段
for (size_t i = 0; i < mylines.size(); i++)
{
Vec4i l = mylines[i];
line(dstImage, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(23, 180, 55), 1, LINE_AA);
}
imshow("【效果图】", dstImage);
}
源码:
#include
#include
using namespace std;
using namespace cv;
#define WINDOW_NAME "【程序窗口】"
Mat g_srcImage, g_dstImage;
Mat g_map_x, g_map_y;
int update_map(int key);
int main(int argc, char** argv)
{
g_srcImage = imread("poster_IronMan.jpg");
imshow("【原始图】", g_srcImage);
g_dstImage.create(g_srcImage.size(), g_srcImage.type());
g_map_x.create(g_srcImage.size(), CV_32FC1);
g_map_y.create(g_srcImage.size(), CV_32FC1);
namedWindow(WINDOW_NAME);
//轮询按键,进行重映射操作并显示效果图
while (1)
{
int key = waitKey(10);
if (key == 27) break;
update_map(key);
remap(g_srcImage, g_dstImage, g_map_x, g_map_y, INTER_LINEAR);
imshow(WINDOW_NAME, g_dstImage);
}
return 0;
}
int update_map(int key)
{
for (int j = 0; j < g_srcImage.rows; j++)
{
for (int i = 0; i < g_srcImage.cols; i++)
{
switch (key)
{
case'1':
if (i > g_srcImage.cols*0.25&&i0.75 &&
j>g_srcImage.rows*0.25 && j < g_srcImage.rows*0.75) {
g_map_x.at<float>(j, i) = static_cast<float>(2*(i-g_srcImage.cols*0.25) + 0.5);
g_map_y.at<float>(j, i) = static_cast<float>(2 * (j - g_srcImage.rows*0.25) + 0.5);
}
else {
g_map_x.at<float>(j, i) = 0;
g_map_y.at<float>(j, i) = 0;
}
break;
case'2':
g_map_x.at<float>(j, i) = static_cast<float>(i);
g_map_y.at<float>(j, i) = static_cast<float>(g_srcImage.rows-j);
break;
case'3':
g_map_x.at<float>(j, i) = static_cast<float>(g_srcImage.cols-i);
g_map_y.at<float>(j, i) = static_cast<float>(j);
break;
case'4':
g_map_x.at<float>(j, i) = static_cast<float>(g_srcImage.cols - i);
g_map_y.at<float>(j, i) = static_cast<float>(g_srcImage.rows - j);
break;
}
}
}
return 1;
}
源码:
#include
#include
using namespace cv;
using namespace std;
#define WINDOW_NAME1 "【原始图窗口】"
#define WINDOW_NAME2 "【经过Warp后的图像】"
#define WINDOW_NAME3 "【经过Warp和Rotate后的图像】"
int main()
{
Point2f srcTriangle[3];
Point2f dstTriangle[3];
Mat rotMat(2, 3, CV_32FC1);
Mat warpMat(2, 3, CV_32FC1);
Mat srcImage, dstImage_warp, dstImage_warp_rotate;
srcImage = imread("poster_landscape_2.jpg");
dstImage_warp = Mat::zeros(srcImage.rows, srcImage.cols, srcImage.type());
//设置源图像和目标图像上的三组点以计算仿射变换
srcTriangle[0] = Point2f(0, 0);
srcTriangle[1] = Point2f(static_cast<float>(srcImage.cols-1), 0);
srcTriangle[2] = Point2f(0, static_cast<float>(srcImage.rows - 1));
dstTriangle[0] = Point2f(static_cast<float>(srcImage.cols*0.0), static_cast<float>(srcImage.rows*0.33));
dstTriangle[1] = Point2f(static_cast<float>(srcImage.cols*0.65), static_cast<float>(srcImage.rows*0.35));
dstTriangle[2] = Point2f(static_cast<float>(srcImage.cols*0.15), static_cast<float>(srcImage.rows*0.6));
warpMat = getAffineTransform(srcTriangle, dstTriangle);
warpAffine(srcImage, dstImage_warp, warpMat, dstImage_warp.size());
//指定缩放旋转来实现仿射变换
Point center = Point(dstImage_warp.cols/2, dstImage_warp.rows/2);
double angle = -30.0;
double scale = 0.8;
rotMat = getRotationMatrix2D(center, angle, scale);
warpAffine(dstImage_warp, dstImage_warp_rotate, rotMat, dstImage_warp.size());
//显示结果
imshow(WINDOW_NAME1, srcImage);
imshow(WINDOW_NAME2, dstImage_warp);
imshow(WINDOW_NAME3, dstImage_warp_rotate);
while (waitKey(3) != 27);
return 0;
}
源码:
#include
#include
using namespace cv;
using namespace std;
int main()
{
Mat srcImage, dstImage;
srcImage = imread("poster_landscape_3.jpg");
cvtColor(srcImage, srcImage, COLOR_BGR2GRAY);
imshow("【原始图】", srcImage);
equalizeHist(srcImage, dstImage);
imshow("【效果图】", dstImage);
while (waitKey(5) != 27);
return 0;
}