目录:
*第一节——基础——加载imread、修改、显示imshow、保存imwrite
*第二节——矩阵的掩模操作filter2D+++代码的运行时间getTickCount
*第三节——Mat对象create、clone、copyTo、cvtColor、cout
*第四节——图像操作——访问图像像素点进行反差操作bitwise_not
*第五节——图像混合——两张大小相等类型相同的图像进行线性混合操作addWeighted
*第六节——提高图像亮度与对比度
*第七节——绘制形状与文字-线line、矩形rectangle、椭圆ellipse、圆circle、多边形fillPoly、随机线与文字
*第八节——图像模糊-去噪-均值blur、高斯GaussianBlur、中值medianBlur、双边bilateralFilter
*第九节——形态学操作——膨胀dilate和腐蚀erode
*第十节——形态学操作morphologyEx——开操作、闭操作、形态学梯度、顶帽、黑帽
*第十一节——形态学操作应用-提取水平与垂直线
*第十二节——图像金字塔——图像上采样pyrUp(zoom in放大)和降采样pyrDown(zoom out缩小)
*第十三节——基本阈值操作threshold
*第十四节——自定义线性滤波——Robert、Sobel、拉普拉斯算子、filter2D
*第十五节——边缘处理——copyMakeBorder用途:在卷积时边界的处理方式有4种类型
*第十六节——Sobel——边缘处理
*第十七节——Laplacian——边缘处理
*第十八节——Canny——边缘处理
*第十九节——霍夫变换——直线变换HoughLinesP
*第二十节——霍夫变换——圆变换HoughCircles
*第二十一节——像素重映射remap
*第二十二节——直方图均衡化equalizeHist
*第二十三节——直方图计算calcHist
*第二十四节——直方图比较compareHist
*第二十五节——直方图反向投影calcBackProject
*第二十六节——模板匹配matchTemplate
*第二十七节——轮廓发现findContours
*第二十八节——凸包convexHull
*第二十九节——轮廓周围绘制矩形框和圆形框
*第三十节——图像矩moments、面积contourArea, 弧长arcLength
*第三十一节——点多边形测试
*第三十二节——点多边形测试
///第一节——基础——加载imread、修改、显示imshow、保存imwrite*///
//添加头文件
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include <opencv2/core/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui/highgui.hpp>
//命名空间
using namespace cv;
using namespace std;
//程序入口
int main(int argc, char** argv)
{
//加载图像
Mat src = imread("E:/images/test.jpg");
if (src.empty()) {printf("error\n"); return -1;}
//if(!src1.data) {cout << "error..." << endl; return -1;}//不同的表达方式
///CV_Assert(testImage.depth() == CV_8U);//若括号中的表达式值为false,则返回一个错误信息_同上一句的效果
namedWindow("opencv setup demo", CV_WINDOW_AUTOSIZE);//创建一个显示窗口
imshow("opencv setup demo", src);//显示图像
//修改图像
Mat dst;
cvtColor(src, dst, CV_BGR2HSV);//色彩空间转换
imshow("output window", dst);//显示修改后的图像
//保存图像
imwrite("E:/images/repair.png", dst);
waitKey(0);
return 0;
}
///第二节——矩阵的掩模操作filter2D+++代码的运行时间getTickCount*/
int main(int argc, char** argv)
{
//代码运行开始时间
double t = getTickCount();
//使用Filter2D函数——掩模操作
Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
filter2D(src, dst, src.depth(), kernel);
//filter2D(src, csrc, -1, kernel);//-1默认和src的深度一样
//代码运行时间=结束时间-开始时间 //转换时间单位并输出代码运行时间
double timeconsume = (getTickCount() - t) / getTickFrequency();
printf("timeconsume %.2f\n", timeconsume);//输出消耗的时间
}
///第三节——Mat对象create、clone、copyTo、cvtColor、cout*/
int main(int argc, char** argv)
{
//创建一个与输入图像src大小、类型一致的图像,且初始化像素为红色
Mat dst = Mat(src.size(), src.type(), Scalar(0, 0, 255));//初始化的像素值,(0, 0, 255)表示红色
//同上的另一种创建方法
Mat m1;
m1.create(src.size(), src.type());
m1 = Scalar(0, 0, 255);
//克隆
Mat dst = src.clone();
//复制
Mat dst;//clone copyTo 两种
src.copyTo(dst);
//将图像转为灰度图像,并显示:输入图像和输出图像的通道结果
cvtColor(src, dst, CV_BGR2GRAY);
printf("input image channels : %d\n", src.channels()); //3通道
printf("output image channels : %d\n", dst.channels()); //1通道
//显示输出图像的宽和高以及输出图像第一个像素值
cvtColor(src, dst, CV_BGR2GRAY);
int cols = dst.cols;//dst的全部列(宽)
int rows = dst.rows;//dst的全部行(高)
printf("rows : %d cols : %d\n", rows, cols);
const uchar* firstRow = dst.ptr<uchar>(0);//访问图像的像素值
printf("fist pixel value : %d\n", *firstRow);//第一个像素值是多少
//定义一个3×3的图像,数据结构为CV_8UC3
Mat M(3, 3, CV_8UC3, Scalar(0,0,255));//3通道,(0,0,255)
imshow("out image", M);
cout << "M =" << endl << M << endl;//一种打印方式
//定义一个100×100的图像,数据结构为CV_8UC1
Mat M(100, 100, CV_8UC1, Scalar(127)); //1通道 127
cout << "M =" << endl << M << endl;
//创建新的窗口——全部初始化为0,图像为黑漆漆的一片,类似于眼睛
Mat m1 = Mat::zeros(2, 2, CV_8UC1);//参数(row,col,type)(行-高,列-宽,类型)
Mat m2 = Mat::zeros(src.size(), src.type());//和原图大小一样
Mat m3 = Mat::eye(2, 2, CV_8UC1);
cout << "m2 =" << endl << m2 << endl;
}
///第四节——图像操作——访问图像像素点进行反差操作bitwise_not*/
int main(int argc, char** args)
{
//单通道图像反差
Mat grayImg;
cvtColor(src, grayImg, CV_BGR2GRAY);//cvtColor(src, grayImg, COLOR_BGR2GRAY);
// int height = grayImg.rows;//行
// int width = grayImg.cols;//列
// for (int row = 0; row < height; row++)
// {
// for (int col = 0; col < width; col++)
// {
// int gray = grayImg.at<uchar>(row, col);
// grayImg.at<uchar>(row, col) = 255 - gray;//反差之后的像素
// }
// }
bitwise_not(grayImg, grayImg); //调用函数等同于for循环的内容 反差图片
//三通道——彩色图像的反差处理
Mat dst;
dst.create(src.size(), src.type());
int height3 = src.rows; int width3 = src.cols; int channels = src.channels();//获取通道
printf("height=%d width=%d channels=%d\n", height, width, channels);
// for (int row = 0; row < height3; row++)
// {
// for (int col = 0; col < width3; col++)
// {
// if (channels == 1)
// {
// int gray = grayImg.at<uchar>(row, col);
// grayImg.at<uchar>(row, col) = 255 - gray;//反差之后的像素
// }
// else if (channels == 3)
// {
// int b = src.at<Vec3b>(row, col)[0];//blue
// int g = src.at<Vec3b>(row, col)[1];//green
// int r = src.at<Vec3b>(row, col)[2];//red
// dst.at<Vec3b>(row, col)[0] = 255 - b;
// dst.at<Vec3b>(row, col)[1] = 255 - g;
// dst.at<Vec3b>(row, col)[2] = 255 - r;
// }
// }
// }
bitwise_not(src, dst); //调用函数等同于for循环的内容 反差图片
}
///第五节——图像混合——两张大小相等类型相同的图像进行线性混合操作addWeighted*/
int main(int argc, char** argv)
{
double alpha = 0.5;
if (src1.rows == src2.rows && src1.cols == src2.cols && src1.type() == src2.type())
{
//add(src1,src2,dst,Mat()); //直接添加的效果
//multiply(src1, src2, dst, 1.0); //相乘的效果
addWeighted(src1, alpha, src2, (1.0 - alpha), 0.0, dst);//线性混合操作
}
else
{
printf("could not blend images , the size of images is not same...\n");
return -1;
}
}
///第六节——提高图像亮度与对比度*/
int main(int argc, char** argv)
{
int height = src.rows;
int width = src.cols;
Mat dst = Mat::zeros(src.size(), src.type());//创建一个和原图像大小和类型一致的空白图像,像素初始化为0
float alpha = 1.5;
float beta = 10;
Mat m1;
src.convertTo(m1, CV_32F);
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
if (src.channels() == 3)
{
float b = m1.at<Vec3f>(row, col)[0];// blue
float g = m1.at<Vec3f>(row, col)[1]; // green
float r = m1.at<Vec3f>(row, col)[2]; // red
// output
dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(b*alpha + beta);
dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(g*alpha + beta);
dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(r*alpha + beta);
}
else if (src.channels() == 1)
{
float v = src.at<uchar>(row, col);
dst.at<uchar>(row, col) = saturate_cast<uchar>(v*alpha + beta);
}
}
}
}
///第七节——绘制形状-线line、矩形rectangle、椭圆ellipse、圆circle、多边形fillPoly、随机线与文字*/
Mat src;//定义的全局变量
const char* drawdemo_win = "draw shapes and text demo";
//函数声明
void MyLines();/*线*/ void MyRectangle();/*矩形*/ void MyEllipse();/*椭圆*/
void MyCircle();/*圆*/ void MyPolygon();/*多边形*/ void RandomLineDemo();/*随机线*/
int main(int argc, char** argv)
{
//函数调用
MyLines();
MyRectangle();
MyEllipse();
MyCircle();
MyPolygon();//多边形
//绘制文字
putText(src, "Hello OpenCV", Point(300, 300),CV_FONT_HERSHEY_COMPLEX, 1.0,Scalar(12, 23, 200), 3, 8);
RandomLineDemo();
}
//函数实现
void MyLines()
{
Point p1 = Point(20, 30);
Point p2;
p2.x = 400;
p2.y = 400;
Scalar color = Scalar(0, 0, 255);//红色
line(src, p1, p2, color, 1, LINE_AA);//1表示线宽
}
void MyRectangle()
{
Rect rect = Rect(200, 100, 300, 300);
Scalar color = Scalar(255, 0, 0);//蓝色
rectangle(src, rect, color, 2, LINE_8);
}
void MyEllipse()
{
Scalar color = Scalar(0, 255, 0);//绿色
/*空心椭圆(src,点中心,尺寸轴,双角度,双起始角,双终止角,颜色,线宽,线型,int shift=0)*/
ellipse(src,Point(src.cols / 2, src.rows / 2),Size(src.cols / 4, src.rows / 8),
90, 0, 360, color, 2, LINE_8);
}
void MyCircle()
{
Scalar color = Scalar(0, 255, 255);
Point center = Point(src.cols / 2, src.rows / 2);
circle(src, center, 150, color, 2, 8);
}
void MyPolygon()
{
Point pts[1][5];
pts[0][0] = Point(100, 100);
pts[0][1] = Point(100, 200);
pts[0][2] = Point(200, 200);
pts[0][3] = Point(200, 100);
pts[0][4] = Point(100, 100);
const Point* ppts[] = { pts[0] };//多边形的顶点
int npt[] = { 5 };//要绘制的多边形顶点数目
Scalar color = Scalar(255, 12, 255);//颜色
fillPoly(src, ppts, npt, 1, color, 8);//1表示画一个多边形-实心的
}
void RandomLineDemo()
{
RNG rng(12345);//随机数生成函数RNG 12345是随机种子
Point pt1;
Point pt2;
Mat bg = Mat::zeros(src.size(), src.type());//创建一张空白图片,纯黑色
for (int i = 0; i < 100000; i++)
{
pt1.x = rng.uniform(0, src.cols);
pt2.x = rng.uniform(0, src.cols);
pt1.y = rng.uniform(0, src.rows);
pt2.y = rng.uniform(0, src.rows);
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
if (waitKey(50) > 0) //随时可以停止的命令
{
break;
}
line(bg, pt1, pt2, color, 1, 8);
}
}
///第八节——图像模糊-去噪-均值blur、高斯GaussianBlur、中值medianBlur、双边bilateralFilter*/
int main(int argc, char** argv)
{
//均值模糊
blur(src, dst, Size(11, 11), Point(-1, -1));//Point(-1, -1)表示默认正中心
//高斯模糊
GaussianBlur(src, gblur, Size(11, 11), 11, 11);//Size(x,y),x,y必须是正数而且是奇数
//中值滤波 对椒盐噪声有很好的效果
medianBlur(src, dst, 3);
//双边滤波 立体感更强
bilateralFilter(src, dst, 15, 200, 5);
//在dst的基础上进行掩模操作提升对比度
Mat resultImg;
Mat kernel = (Mat_<int>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
filter2D(dst, resultImg, -1, kernel, Point(-1, -1), 0);
}
///第九节——形态学操作——膨胀dilate和腐蚀erode*/
int element_size = 3;//为了实现动态调整大小的目的,设置结构元素的大小
int max_size = 21;
void CallBack_Demo(int, void*);
int main(int argc, char** argv)
{
createTrackbar("Element Size :", OUTPUT_WIN, &element_size, max_size, CallBack_Demo);
createTrackbar("Element Size :", OUTPUT_WIN2, &element_size, max_size, CallBack_Demo);
CallBack_Demo(0, 0);//函数调用
}
void CallBack_Demo(int, void*)
{
//结构元素
int s = element_size * 2 + 1;
Mat structureElement = getStructuringElement(MORPH_RECT, Size(s, s), Point(-1, -1));
//膨胀_变小
dilate(src, dst1, structureElement, Point(-1, -1), 1);
//腐蚀_变大
erode(src, dst2, structureElement);
}
///第十节——形态学操作morphologyEx——开操作、闭操作、形态学梯度、顶帽、黑帽*/
int main(int argc, char** argv)
{
Mat kernel = getStructuringElement(MORPH_RECT, Size(11,11), Point(-1, -1));//结构元素
morphologyEx(src, dst, CV_MOP_OPEN, kernel);//开操作——先腐蚀后膨胀(去掉小的对象)
morphologyEx(src, dst, CV_MOP_CLOSE, kernel);//闭操作——先膨胀后腐蚀(填充小的洞)
morphologyEx(src, dst, CV_MOP_GRADIENT, kernel);//形态学梯度——膨胀减去腐蚀
morphologyEx(src, dst, CV_MOP_TOPHAT, kernel);//顶帽——原图像与开操作之间的差值图像
morphologyEx(src, dst, CV_MOP_BLACKHAT, kernel);//黑帽——闭操作图像与源图像的差值图像
}
///第十一节——形态学操作应用-提取水平与垂直线*/
int main(int argc, char** argv)
{
/*
转换为二值图像的函数说明
adaptiveThreshold
(Mat src, // 输入的灰度图像
Mat dst, // 二值图像
double maxValue, // 二值图像最大值
int adaptiveMethod // 自适应方法,二选一,只有这两种ADAPTIVE_THRESH_MEAN_C,ADAPTIVE_THRESH_GAUSSIAN_C
int thresholdType, // 阈值类型
int blockSize, // 块大小
double C) // 常量C 可以是正数,0,负数
*/
cvtColor(src, gray_src, CV_BGR2GRAY);
Mat binImg;//二值图像
adaptiveThreshold(~gray_src, binImg, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);
Mat temp;
/// 水平结构元素
Mat hline = getStructuringElement(MORPH_RECT, Size(src.cols / 16, 1), Point(-1, -1));
erode(binImg, temp, hline);//腐蚀
dilate(temp, dst, hline);//膨胀 /*morphologyEx(binImg, dst, CV_MOP_OPEN, hline);//先腐蚀后膨胀*/
bitwise_not(dst, dst);//反差图片
blur(dst, dst, Size(3, 3), Point(-1, -1));
/// 垂直结构元素
Mat vline = getStructuringElement(MORPH_RECT, Size(1, src.rows / 16), Point(-1, -1));
erode(binImg, temp, vline);//腐蚀
dilate(temp, dst, vline);//膨胀/*morphologyEx(binImg, dst, CV_MOP_OPEN, vline);//先膨胀后腐蚀*/
bitwise_not(dst, dst);//反差图片
blur(dst, dst, Size(3, 3), Point(-1, -1));
/// 矩形结构元素
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
erode(binImg, temp, kernel);//腐蚀
dilate(temp, dst, kernel);//膨胀/*morphologyEx(binImg, dst, CV_MOP_OPEN, kernel);*/
bitwise_not(dst, dst);//反差图片
blur(dst, dst, Size(3, 3), Point(-1, -1));
}
///第十二节——图像金字塔——图像上采样pyrUp(zoom in放大)和降采样pyrDown(zoom out缩小)*/
int main(int agrc, char** argv)
{
// 上采样——生成的图像是原图在宽与高各放大两倍
pyrUp(src, dst1, Size(src.cols * 2, src.rows * 2));
// 降采样——生成的图像是原图在宽与高各缩小1/2
pyrDown(src, dst2, Size(src.cols / 2, src.rows / 2));
/*
DOG高斯不同,可以应用到轮廓提取
——就是把同一张图像在不同的参数下做高斯模糊之后的结果相减,得到的输出图像
*/
Mat gray_src, g1, g2, dogImg;
cvtColor(src, gray_src, CV_BGR2GRAY);//原图-灰度图
GaussianBlur(gray_src, g1, Size(5, 5), 0, 0);//灰度图-g1
GaussianBlur(g1, g2, Size(5, 5), 0, 0);//g1-g2
subtract(g1, g2, dogImg, Mat());//g1减去g2 得到的图像dogImg
normalize(dogImg, dogImg, 255, 0, NORM_MINMAX);// 归一化显示
}
///第十三节——基本阈值操作threshold*/
Mat src, gray_src, dst;
int threshold_value = 127;//无厘头的值=255/2
int threshold_max = 255;//阈值最大值
int type_value = 2;
int type_max = 4;//五种方法
const char* output_title = "binary image";
void Threshold_Demo(int, void*);//定义一个变量函数
int main(int argc, char** argv)
{
createTrackbar("Threshold Value:", output_title, &threshold_value, threshold_max, Threshold_Demo);
createTrackbar("Type Value:", output_title, &type_value, type_max, Threshold_Demo);
Threshold_Demo(0, 0);
}
void Threshold_Demo(int, void*)
{
cvtColor(src, gray_src, CV_BGR2GRAY);//灰度图
/*
threshold(src,dst,当前阈值,阈值最大值,阈值类型)
THRESH_BINARY、THRESH_BINARY_INV、THRESH_TRUNC、THRESH_TOZERO、THRESH_TOZERO_INV
*/
//自动计算阈值1——THRESH_OTSU、THRESH_TRIANGLE表示自动方法——调节createTrackbar时没有反应的
threshold(gray_src, dst, 0, 255, THRESH_OTSU | type_value);
//自动计算阈值2————type_value表示在5种阈值类型中选择哪一种阈值类型
threshold(gray_src, dst, 0, 255, THRESH_TRIANGLE | type_value);、
//表示阈值类型为THRESH_BINARY(type_value=0)
threshold(gray_src, dst, threshold_value, threshold_max, THRESH_BINARY);
//显示五种 阈值方法
threshold(gray_src, dst, threshold_value, threshold_max, type_value);
}
///第十四节——自定义线性滤波——边缘——Robert、Sobel、拉普拉斯算子、filter2D*/
int main(int argc, char** argv)
{
/*
filter2D(
Mat src, //输入图像
Mat dst, // 模糊图像
int depth, // 图像深度32/8(取-1)
Mat kernel, // 卷积核/模板——kernel是可以自定义的卷积核
Point anchor, // 锚点位置
double delta // 计算出来的像素+delta)
*/
///边缘检测的算子
//Robert算子 X方向
Mat Robert_dst_x;
Mat Robert_kernel_x = (Mat_<int>(2, 2) << 1, 0, 0, -1);
filter2D(src, Robert_dst_x, -1, Robert_kernel_x, Point(-1, -1), 0.0);
//Robert算子 Y方向
Mat Robert_dst_y;
Mat Robert_kernel_y = (Mat_<int>(2, 2) << 0, 1, -1, 0);
filter2D(src, Robert_dst_y, -1, Robert_kernel_y, Point(-1, -1), 0.0);
//Sobel X 方向
Mat Sobel_dst_x;
Mat Sobel_kernel_x = (Mat_<int>(3, 3) << -1, 0, 1, -2, 0, 2, -1, 0, 1);
filter2D(src, Sobel_dst_x, -1, Sobel_kernel_x, Point(-1, -1), 0.0);
//Sobel Y 方向
Mat Sobel_dst_y;
Mat Sobel_kernel_y = (Mat_<int>(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1);
filter2D(src, Sobel_dst_y, -1, Sobel_kernel_y, Point(-1, -1), 0.0);
//拉普拉斯算子
Mat lpls_kernel = (Mat_<int>(3, 3) << 0, -1, 0, -1, 4, -1, 0, -1, 0);
filter2D(src, dst, -1, lpls_kernel, Point(-1, -1), 0.0);
///自定义卷积模糊
int c = 0;
int index = 0;
int ksize = 0;
while (true)
{
c = waitKey(500);
if ((char)c == 27)
{
break;// ESC
}
ksize = 5 + (index % 8) * 2;
Mat kernel = Mat::ones(Size(ksize, ksize), CV_32F) / (float)(ksize * ksize);
filter2D(src, dst, -1, kernel, Point(-1, -1));
index++;
imshow("OUTPUT_WIN", dst);
}
}
///第十五节——边缘处理——copyMakeBorder用途:在卷积时边界的处理方式有4种类型*/
int main(int argc, char** argv)
{
//定义上下左右边
int top = (int)(0.05*src.rows);
int bottom = (int)(0.05*src.rows);
int left = (int)(0.05*src.cols);
int right = (int)(0.05*src.cols);
RNG rng(12345);//随机数+随机种子
int borderType = BORDER_DEFAULT;//边界类型(4种)
/*BORDER_DEFAULT、BORDER_CONSTANT、BORDER_REPLICATE、BORDER_WRAP*/
int c = 0;
while (true)
{
c = waitKey(500);
// ESC 27代表Esc退出键
if ((char)c == 27)//退出来的条件
{
break;
}
if ((char)c == 'r')
{
borderType = BORDER_REPLICATE;
}
else if ((char)c == 'w')
{
borderType = BORDER_WRAP;
}
else if ((char)c == 'c')
{
borderType = BORDER_CONSTANT;
}
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
copyMakeBorder(src, dst, top, bottom, left, right, borderType, color);//创建边界,得到显示
imshow(“OUTPUT_WIN”, dst);
}//按键显示各种类型的效果
Mat dst1;//高斯模糊的效果显示
GaussianBlur(src, dst1, Size(5, 5), 0, 0,BORDER_REPLICATE);
GaussianBlur(src, dst1, Size(5, 5), 0, 0,BORDER_WRAP);//不支持
GaussianBlur(src, dst1, Size(5, 5), 0, 0,BORDER_DEFAULT);//默认处理方法
GaussianBlur(src, dst1, Size(5, 5), 0, 0,BORDER_CONSTANT);
}
///第十六节——Sobel——边缘处理*/
int main(int argc, char** argv)
{
//高斯模糊→灰度图→求梯度X和Y→振幅图像
GaussianBlur(src, dst, Size(3, 3), 0, 0);//高斯模糊
cvtColor(dst, gray_src, CV_BGR2GRAY);//灰度图
//X方向和Y方向的梯度
Mat xgrad, ygrad;
//输入图像-输出图像-输出图像深度大于原来的-x方向-y方向-ksize
Sobel(gray_src, xgrad, CV_16S, 1, 0, 3);
Sobel(gray_src, ygrad, CV_16S, 0, 1, 3);
/*
// 改进版本
Scharr(gray_src, xgrad, CV_16S, 1, 0);
Scharr(gray_src, ygrad, CV_16S, 0, 1);
*/
convertScaleAbs(xgrad, xgrad);// 计算图像A的像素绝对值,输出到图像B
convertScaleAbs(ygrad, ygrad);
//振幅图像 //两幅图叠加
Mat x_ygrad;
addWeighted(xgrad, 0.5, ygrad, 0.5, 0, x_ygrad);
/*****公式效果 可直接使用addWeighted*****/
// Mat xygrad = Mat(xgrad.size(), xgrad.type());
// printf("type : %d\n", xgrad.type());
// int width = xgrad.cols;
// int height = ygrad.rows;
// for (int row = 0; row < height; row++)
// {
// for (int col = 0; col < width; col++)
// {
// int xg = xgrad.at<uchar>(row, col);
// int yg = ygrad.at<uchar>(row, col);
// int xy = xg + yg;
// xygrad.at<uchar>(row, col) = saturate_cast<uchar>(xy);
// }
// }
}
///第十七节——Laplacian——边缘处理*/
int main(int argc, char** argv)
{
Mat gray_src, edge_image;
GaussianBlur(src, dst, Size(3, 3), 0, 0);
cvtColor(dst, gray_src, CV_BGR2GRAY);
Laplacian(gray_src, edge_image, CV_16S, 3);
convertScaleAbs(edge_image, edge_image);
threshold(edge_image, edge_image, 0, 255, THRESH_OTSU | THRESH_BINARY);
}
///第十八节——Canny——边缘处理*/
int t1_value = 50;
int max_value = 255;
void Canny_Demo(int, void*);
int main(int argc, char** argv)
{
cvtColor(src, gray_src, CV_BGR2GRAY);//灰度化
createTrackbar("Threshold Value:", OUTPUT_TITLE, &t1_value, max_value, Canny_Demo);//创建阈值滑块
Canny_Demo(0, 0);
}
void Canny_Demo(int, void*)
{
Mat edge_dst;
blur(gray_src, gray_src, Size(3, 3), Point(-1, -1), BORDER_DEFAULT);//均值模糊
Canny(gray_src, edge_dst, t1_value, t1_value * 2, 3, false);
imshow("output_title1", edge_dst);//黑底白边
imshow("OUTPUT_TITLE2", ~edge_dst);//取反,白底黑边
dst.create(src.size(), src.type());
src.copyTo(dst, edge_dst);//对图像进行初始化
imshow("output_title13", dst);//彩色图
}
///第十九节——霍夫变换——直线变换HoughLinesP*/
/
霍夫变换是图像处理中从图像中识别几何形状的基本方法之一,
主要用来从图像中分离出具有某种相同特征的几何形状(如:直线、圆)。
做基本的霍夫变换是从黑白图像中检测直线
/
int main(int argc, char** argv)
{
// extract edge
Canny(src, canny_dst, 150, 200);//Canny边缘检测
cvtColor(canny_dst, dst, CV_GRAY2BGR);
//HoughLinesP
vector<Vec4f> plines;//定义一个浮点数-霍夫变换得出的结果放入plines数组中
HoughLinesP(src_gray, plines, 1, CV_PI / 180.0, 10, 0, 30);
Scalar color = Scalar(0, 0, 255);
for (size_t i = 0; i < plines.size(); i++) //size_t看成int
{
Vec4f hline = plines[i];
line(dst, Point(hline[0], hline[1]), Point(hline[2], hline[3]), color, 3, LINE_AA);
}
}
///***********第二十节——霍夫变换——圆变换HoughCircles************/
```cpp
int main(int argc, char** argv)
{
// 中值滤波
medianBlur(src, medianBlur_output, 3);
cvtColor(medianBlur_output, medianBlur_output, CV_BGR2GRAY);
// 霍夫圆检测
vector<Vec3f> pcircles;
HoughCircles(medianBlur_output, pcircles, CV_HOUGH_GRADIENT, 1, 10, 100, 30, 5, 50);
src.copyTo(dst);
for (size_t i = 0; i < pcircles.size(); i++)
{
Vec3f cc = pcircles[i];
circle(dst, Point(cc[0], cc[1]), cc[2], Scalar(0, 0, 255), 2, LINE_AA);//圆的位置
circle(dst, Point(cc[0], cc[1]), 2, Scalar(198, 23, 155), 2, LINE_AA);//圆心位置
}
imshow(OUTPUT_TITLE, dst);
}
///第二十一节——像素重映射remap*/
Mat src, dst, map_x, map_y;
const char* OUTPUT_TITLE = "remap demo";
int index = 0;
void update_map(void);
int main(int argc, char** argv)
{
//建立映射表 和输入图像的大小一致 32位单通道图像
map_x.create(src.size(), CV_32FC1);
map_y.create(src.size(), CV_32FC1);
int c = 0;
while (true)
{
c = waitKey(500);
if ((char)c == 27) //Esc
{
break;
}
index = c % 4;//取模
update_map();
remap(src, dst, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 255, 255));
imshow(OUTPUT_TITLE, dst);
}
return 0;
}
void update_map(void)
{
for (int row = 0; row < src.rows; row++)
{
for (int col = 0; col < src.cols; col++)
{
switch (index)
{
case 0://缩小一半
if (col >(src.cols * 0.25) && col <= (src.cols*0.75) && row >(src.rows*0.25) && row <= (src.rows*0.75))
{
map_x.at<float>(row, col) = 2 * (col - (src.cols*0.25));
map_y.at<float>(row, col) = 2 * (row - (src.rows*0.25));
}
else
{
map_x.at<float>(row, col) = 0;
map_y.at<float>(row, col) = 0;
}
break;
case 1://X方向对调
map_x.at<float>(row, col) = (src.cols - col - 1);
map_y.at<float>(row, col) = row;
break;
case 2://Y方向对调
map_x.at<float>(row, col) = col;
map_y.at<float>(row, col) = (src.rows - row - 1);
break;
case 3://XY方向同时对调
map_x.at<float>(row, col) = (src.cols - col - 1);
map_y.at<float>(row, col) = (src.rows - row - 1);
break;
}
}
}
}
///第二十二节——直方图均衡化equalizeHist*/
int main(int argc, char** argv)
{
cvtColor(src, src, CV_BGR2GRAY);
equalizeHist(src, dst);
}
///第二十三节——直方图计算calcHist*/
int main(int argc, char** argv)
{
// 分通道显示
vector<Mat> bgr_planes;//vector数组
split(src, bgr_planes);
/*
imshow("single channel demo1", bgr_planes[0]);
imshow("single channel demo2", bgr_planes[1]);
imshow("single channel demo3", bgr_planes[2]);
*/
// 计算直方图
int histSize = 256;
float range[] = { 0, 256 };
const float *histRanges = { range };
Mat b_hist, g_hist, r_hist;
calcHist(&bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRanges, true, false);
calcHist(&bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRanges, true, false);
calcHist(&bgr_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, &histRanges, true, false);
// 归一化 → 为了显示
int hist_h = 400;
int hist_w = 512;
int bin_w = hist_w / histSize;
Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));
normalize(b_hist, b_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
normalize(g_hist, g_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
normalize(r_hist, r_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
// render histogram chart 绘制直方图
for (int i = 1; i < histSize; i++)
{
line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(b_hist.at<float>(i - 1))),
Point((i)*bin_w, hist_h - cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0), 2, LINE_AA);
line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(g_hist.at<float>(i - 1))),
Point((i)*bin_w, hist_h - cvRound(g_hist.at<float>(i))), Scalar(0, 255, 0), 2, LINE_AA);
line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(r_hist.at<float>(i - 1))),
Point((i)*bin_w, hist_h - cvRound(r_hist.at<float>(i))), Scalar(0, 0, 255), 2, LINE_AA);
}
imshow(OUTPUT_T, histImage);
}
///第二十四节——直方图比较compareHist*/
string convertToString(double d);
int main(int argc, char** argv)
{
Mat base, test1, test2;
Mat hsvbase, hsvtest1, hsvtest2;
base = imread("F:/images/test.png");
test1 = imread("F:/images/rose.png");
test2 = imread("F:/images/rosenoise.png");
if (!base.data||!test1.data||!test2.data)
{
printf("could not load image...\n");
return -1;
}
//色彩空间转换
cvtColor(base, hsvbase, CV_BGR2HSV);
cvtColor(test1, hsvtest1, CV_BGR2HSV);
cvtColor(test2, hsvtest2, CV_BGR2HSV);
//定义直方图计算需要的各种参数
//每个维度的bin数目 对Hue通道使用50个bin,对saturation通道使用60个bin
int h_bins = 50;
int s_bins = 60;
int histSize[] = { h_bins, s_bins };
// hue varies from 0 to 179, saturation(饱和度) from 0 to 255
//每个维度的取值范围
float h_ranges[] = { 0, 180 };
float s_ranges[] = { 0, 256 };
const float* ranges[] = { h_ranges, s_ranges };
// Use the o-th and 1-st channels 使用第0和第1通道
int channels[] = { 0, 1 };
//MatND是Mat的别名,方便区分经过直方图计算处理后的输入图像
MatND hist_base;//ND表示多维
MatND hist_test1;
MatND hist_test2;
//计算直方图并进行归一化处理
//参数:输入图像的指针,图像数目,通道数,输入mask可选不用,输出的直方图数据,维数,直方图级数,值域范围,默认,默认)
calcHist(&hsvbase, 1, channels, Mat(), hist_base, 2, histSize, ranges, true, false);
normalize(hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat());
calcHist(&hsvtest1, 1, channels, Mat(), hist_test1, 2, histSize, ranges, true, false);
normalize(hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat());
calcHist(&hsvtest2, 1, channels, Mat(), hist_test2, 2, histSize, ranges, true, false);
normalize(hist_test2, hist_test2, 0, 1, NORM_MINMAX, -1, Mat());
//巴氏距离
double basebase = compareHist(hist_base, hist_base, CV_COMP_BHATTACHARYYA);
double basetest1 = compareHist(hist_base, hist_test1, CV_COMP_BHATTACHARYYA);
double basetest2 = compareHist(hist_base, hist_test2, CV_COMP_BHATTACHARYYA);
double tes1test2 = compareHist(hist_test1, hist_test2, CV_COMP_BHATTACHARYYA);
printf("base compare with base correlation value :%f\n", basebase);
printf("base compare with test1 correlation value :%f\n", basetest1);
printf("base compare with test2 correlation value :%f\n", basetest2);
printf("test1 compare with test2 correlation value :%f\n", tes1test2);
//给每个图像上添加文字,内容为该图片和原始图片的比较结果
Mat test12;
test2.copyTo(test12);
putText(base, convertToString(basebase), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
putText(test1, convertToString(basetest1), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
putText(test2, convertToString(basetest2), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
putText(test12, convertToString(tes1test2), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
//图像的显示
imshow("base", base);
imshow("test1", test1);
imshow("test2", test2);
imshow("test12", test12);
}
string convertToString(double d)
{
ostringstream os;
if (os << d)
return os.str();
return "invalid conversion";
}
///第二十五节——直方图反向投影calcBackProject*/
Mat src; Mat hsv; Mat hue;
int bins = 12;//初始化bin数目
void Hist_And_Backprojection(int, void*);
int main(int argc, char** argv)
{
/*
使用mixChannels来抽取HSV图像的0通道Hue
&hsv→一系列输入图像的数组,被拷贝的通道的来源
1→输入数组中图像的数目
&hue→一系列目的图像的数组,储存拷贝的通道
1→目的数组中图像的数目
nchannels→通道索引对的数组,指示如何将输入图像的某一通道拷贝到目的图像的某一通道,0→0
1→通道索引对的数目
*/
//转换到HSV空间
cvtColor(src, hsv, CV_BGR2HSV);
//分离Hue通道
/*本教程仅使用Hue通道来创建1维直方图
(可以从其他链接下载增强版本,其使用了更常见的H-S直方图,以获得更好的新效果)*/
hue.create(hsv.size(), hsv.depth());
int nchannels[] = { 0, 0 };
mixChannels(&hsv, 1, &hue, 1, nchannels, 1);
//创建Trackbar来输入bin的数目,Trackbar的任何变动将会调用函数Hist_And_Backprojection
createTrackbar("Histogram Bins:", window_image, &bins, 180, Hist_And_Backprojection);
Hist_And_Backprojection(0, 0);
}
void Hist_And_Backprojection(int, void*)/* Trackbar 事件的回调函数*/
{
float range[] = { 0, 180 };
const float *histRanges = { range };
Mat h_hist;
//计算直方图并归一化
calcHist(&hue, 1, 0, Mat(), h_hist, 1, &bins, &histRanges, true, false);
normalize(h_hist, h_hist, 0, 255, NORM_MINMAX, -1, Mat());
//计算反向投影
Mat backPrjImage;
calcBackProject(&hue, 1, 0, h_hist, backPrjImage, &histRanges, 1, true);
//显示反向投影
imshow("BackProj", backPrjImage);
//显示1维Hue直方图
int hist_h = 400;
int hist_w = 400;
Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));
int bin_w = (hist_w / bins);
for (int i = 1; i < bins; i++)
{
rectangle
(histImage,
Point((i - 1)*bin_w, (hist_h - cvRound(h_hist.at<float>(i - 1) * (400 / 255)))),
//Point(i*bin_w, (hist_h - cvRound(h_hist.at<float>(i) * (400 / 255)))),
Point(i*bin_w, hist_h),
Scalar(0, 0, 255), -1);
}
imshow("Histogram", histImage);
return;
}
///第二十六节——模板匹配matchTemplate*/
int match_method = TM_SQDIFF;
int max_track = 5;
void Match_Demo(int, void*);
int main(int argc, char** argv)
{
src = imread("F:/images/test.png");// 待检测图像
temp = imread("F:/images/matchtest.png");// 模板图像
const char* trackbar_title = "Match Algo Type:";
createTrackbar(trackbar_title, OUTPUT_T, &match_method, max_track, Match_Demo);
Match_Demo(0, 0);
}
void Match_Demo(int, void*)
{
int width = src.cols - temp.cols + 1;
int height = src.rows - temp.rows + 1;
Mat result(width, height, CV_32FC1);
//模板匹配+归一化 matchTemplate在模板和输入图像之间寻找匹配获得匹配结果图像
matchTemplate(src, temp, result, match_method, Mat());
normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());
//通过minMaxLoc定位最匹配的位置
Point minLoc; Point maxLoc;
double min, max;
Point temLoc;
//minMaxLoc 在给定的矩阵中寻找最大和最小值(包括他们的位置)
/*
minMaxLoc(匹配结果矩阵,在矩阵result中存储的最小值和最大值,在结果矩阵中最小值和最大值的坐标,可选的掩模)
*/
minMaxLoc(result, &min, &max, &minLoc, &maxLoc, Mat());
//对于方法 TM_SQDIFF和TM_SQDIFF_NORMED ,越小的数值代表更高的匹配结果,而对于其他的方法,数值越大匹配越好
if (match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED)
{
temLoc = minLoc;
}
else
{
temLoc = maxLoc;
}
// 绘制矩形
src.copyTo(dst);
rectangle(dst, Rect(temLoc.x, temLoc.y, temp.cols, temp.rows), Scalar(0, 0, 255), 2, 8);
rectangle(result, Rect(temLoc.x, temLoc.y, temp.cols, temp.rows), Scalar(0, 0, 255), 2, 8);
imshow(OUTPUT_T, result);
imshow(match_t, dst);
}
///第二十七节——轮廓发现findContours*/
int threshold_value = 100;
int threshold_max = 255;
RNG rng;
void Demo_Contours(int, void*);
int main(int argc, char** argv)
{
//灰度化
cvtColor(src, gray, CV_BGR2GRAY);
const char* trackbar_title = "Threshold Value:";
createTrackbar(trackbar_title, output_win, &threshold_value, threshold_max, Demo_Contours);
Demo_Contours(0, 0);
}
void Demo_Contours(int, void*)
{
Mat canny_output;
vector<vector<Point>> contours;
vector<Vec4i> hierachy;
Canny(gray, canny_output, threshold_value, threshold_value * 2, 3, false);
//发现轮廓
findContours(canny_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
//绘制轮廓
dst = Mat::zeros(gray.size(), CV_8UC3);//dst初始化
RNG rng(12345);//随机数+随机种子
for (size_t i = 0; i < contours.size(); i++)
{
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(dst, contours, i, color, 2, 8, hierachy, 0, Point(0, 0));
}
imshow(output_win, dst);
}
///第二十八节——凸包convexHull*/
int threshold_value = 100;
int threshold_max = 255;
void Threshold_Callback(int, void*);
RNG rng(12345);
int main(int argc, char** argv)
{
cvtColor(src, src_gray, CV_BGR2GRAY);
blur(src_gray, src_gray, Size(3, 3), Point(-1, -1), BORDER_DEFAULT);
createTrackbar(trackbar_label, output_win, &threshold_value, threshold_max, Threshold_Callback);
Threshold_Callback(0, 0);
}
void Threshold_Callback(int, void*)
{
Mat bin_output;
vector<vector<Point>> contours;
vector<Vec4i> hierachy;
threshold(src_gray, bin_output, threshold_value, threshold_max, THRESH_BINARY);
findContours(bin_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
vector<vector<Point>> convexs(contours.size());
for (size_t i = 0; i < contours.size(); i++)
{
convexHull(contours[i], convexs[i], true, false);
}
dst = Mat::zeros(src.size(), CV_8UC3);
vector<Vec4i> empty(0);
for (size_t k = 0; k < contours.size(); k++)
{
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(dst, contours, k, color, 2, LINE_8, hierachy, 0, Point(0, 0));
drawContours(dst, convexs, k, color, 2, LINE_8, empty, 0, Point(0, 0));
}
imshow(output_win, dst);
return;
}
///第二十九节——轮廓周围绘制矩形框和圆形框*/
int threshold_v = 170;
int threshold_max = 255;
const char* output_win = "rectangle-demo";
RNG rng(12345);
void Contours_Callback(int, void*);
int main(int argc, char** argv)
{
cvtColor(src, gray_src, CV_BGR2GRAY);
blur(gray_src, gray_src, Size(3, 3), Point(-1, -1));
createTrackbar("Threshold Value:", output_win, &threshold_v, threshold_max, Contours_Callback);
Contours_Callback(0, 0);
}
void Contours_Callback(int, void*)
{
Mat binary_output;//阈值二值化图像
threshold(gray_src, binary_output, threshold_v, threshold_max, THRESH_BINARY);
//轮廓检测参数
vector<vector<Point>> contours;
vector<Vec4i> hierachy;
findContours(binary_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-1, -1));//发现轮廓
vector<vector<Point>> contours_ploy(contours.size());//外接多边形
vector<Rect> ploy_rects(contours.size());//外接矩形
vector<Point2f> ccs(contours.size());//圆心
vector<float> radius(contours.size());//圆的半径
vector<RotatedRect> minRects(contours.size());//最小化矩形
vector<RotatedRect> myellipse(contours.size());//椭圆
for (size_t i = 0; i < contours.size(); i++)
{
//基于RDP算法实现,目的是减少多边形轮廓点数
/* approxPolyDP(InputArray curve,OutputArray approxCurve, double epsilon任意数-表示两点之间的最小距离, bool closed闭合曲线) */
approxPolyDP(Mat(contours[i]), contours_ploy[i], 3, true);
ploy_rects[i] = boundingRect(contours_ploy[i]);//boundingRect——得到轮廓周围最小矩形,左上角和右下角坐标绘制一个矩形
minEnclosingCircle(contours_ploy[i], ccs[i], radius[i]);//得到最小区域圆形
if (contours_ploy[i].size() > 5) //多边形最少要5个点
{
minRects[i] = minAreaRect(contours_ploy[i]);//得到一个旋转的矩形,返回旋转矩形
myellipse[i] = fitEllipse(contours_ploy[i]);//得到最小椭圆
}
}
// draw it
drawImg = Mat::zeros(src.size(), src.type());
Point2f pts[4];
for (size_t t = 0; t < contours.size(); t++)
{
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
/*rectangle(drawImg, ploy_rects[t], color, 2, 8);
circle(drawImg, ccs[t], radius[t], color, 2, 8);*/
if (contours_ploy[t].size() > 5)
{
//绘制矩形
minRects[t].points(pts);
for (int r = 0; r < 4; r++)
{
line(drawImg, pts[r], pts[(r + 1) % 4], color, 1, 8);
}
//绘制椭圆
ellipse(drawImg, myellipse[t], color, 1, 8);
}
}
imshow(output_win, drawImg);
return;
}
///第三十节——图像矩moments、面积contourArea, 弧长arcLength*/
int threshold_value = 80;
int threshold_max = 255;
RNG rng(12345);
void Demo_Moments(int, void*);
int main(int argc, char** argv)
{
cvtColor(src, gray_src, CV_BGR2GRAY);
GaussianBlur(gray_src, gray_src, Size(3, 3), 0, 0);//目的:降低噪声
createTrackbar("Threshold Value : ", output_win, &threshold_value, threshold_max, Demo_Moments);
Demo_Moments(0, 0);
}
void Demo_Moments(int, void*)
{
Mat canny_output;
vector<vector<Point>> contours;//轮廓点
vector<Vec4i> hierachy;
Canny(gray_src, canny_output, threshold_value, threshold_value * 2, 3, false);//边缘提取
findContours(canny_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));//发现轮廓
vector<Moments> contours_moments(contours.size());//矩
vector<Point2f> ccs(contours.size());//中心点
for (size_t i = 0; i < contours.size(); i++)
{
contours_moments[i] = moments(contours[i]);
ccs[i] = Point(static_cast<float>(contours_moments[i].m10 / contours_moments[i].m00),
static_cast<float>(contours_moments[i].m01 / contours_moments[i].m00));
}
Mat drawImg;
src.copyTo(drawImg);
for (size_t i = 0; i < contours.size(); i++)
{
if (contours[i].size() < 100)
{
continue;
}
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
printf("center point x : %.2f y : %.2f\n", ccs[i].x, ccs[i].y);
printf("contours %d area : %.2f arc length : %.2f\n", i, contourArea(contours[i]), arcLength(contours[i], true));
drawContours(drawImg, contours, i, color, 1, 8, hierachy, 0, Point(0, 0));//绘制轮廓
circle(drawImg, ccs[i], 2, color, 1, 8);
}
imshow(output_win, drawImg);
return;
}
///第三十一节——点多边形测试*/
int main(int argc, char** argv)
{
const int r = 100;
Mat src = Mat::zeros(r * 4, r * 4, CV_8UC1);
vector<Point2f> vert(6);//定义6个点
vert[0] = Point(3 * r / 2, static_cast<int>(1.34*r));
vert[1] = Point(1 * r, 2 * r);
vert[2] = Point(3 * r / 2, static_cast<int>(2.866*r));
vert[3] = Point(5 * r / 2, static_cast<int>(2.866*r));
vert[4] = Point(3 * r, 2 * r);
vert[5] = Point(5 * r / 2, static_cast<int>(1.34*r));
//画线
for (int i = 0; i < 6; i++)
{
line(src, vert[i], vert[(i + 1) % 6], Scalar(255), 3, 8, 0);
}
//发现轮廓
vector<vector<Point>> contours;//轮廓点
vector<Vec4i> hierachy;
Mat csrc;
src.copyTo(csrc);
findContours(csrc, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
//测试生成的图
Mat raw_dist = Mat::zeros(csrc.size(), CV_32FC1);
for (int row = 0; row < raw_dist.rows; row++)
{
for (int col = 0; col < raw_dist.cols; col++)
{
double dist = pointPolygonTest(contours[0],
Point2f(static_cast<float>(col), static_cast<float>(row)), true);
raw_dist.at<float>(row, col) = static_cast<float>(dist);
}
}
double minValue, maxValue;
minMaxLoc(raw_dist, &minValue, &maxValue, 0, 0, Mat());//最大和最小坐标位置的寻找
Mat drawImg = Mat::zeros(src.size(), CV_8UC3);//绘制出来的图像
for (int row = 0; row < drawImg.rows; row++)
{
for (int col = 0; col < drawImg.cols; col++)
{
float dist = raw_dist.at<float>(row, col);
if (dist > 0) //大于0表示在内部
{
drawImg.at<Vec3b>(row, col)[0] = (uchar)(abs(1.0 - (dist / maxValue)) * 255);
printf("距离为:%f\n", dist);
}
else if (dist < 0) //小于0表示在外部
{
drawImg.at<Vec3b>(row, col)[2] = (uchar)(abs(1.0 - (dist / minValue)) * 255);
}
else//在多边形边上白色
{
drawImg.at<Vec3b>(row, col)[0] = (uchar)(abs(255 - dist));
drawImg.at<Vec3b>(row, col)[1] = (uchar)(abs(255 - dist));
drawImg.at<Vec3b>(row, col)[2] = (uchar)(abs(255 - dist));
}
}
}
}
///第三十二节——基于距离变换与分水岭的图像分割*/
int main(int argc, char** argv)
{
// 1. change background
for (int row = 0; row < src.rows; row++)
{
for (int col = 0; col < src.cols; col++)
{
//如果是白色的将背景变成黑色
if (src.at<Vec3b>(row, col) == Vec3b(255, 255, 255))
{
src.at<Vec3b>(row, col)[0] = 0;
src.at<Vec3b>(row, col)[1] = 0;
src.at<Vec3b>(row, col)[2] = 0;
}
}
}
// 2. sharpen 图像锐化 拉普拉斯
Mat kernel = (Mat_<float>(3, 3) << 1, 1, 1, 1, -8, 1, 1, 1, 1);
Mat imgLaplance;//拉普拉斯图像
Mat sharpenImg = src;//等于原图
filter2D(src, imgLaplance, CV_32F, kernel, Point(-1, -1), 0, BORDER_DEFAULT);
src.convertTo(sharpenImg, CV_32F);
Mat resultImg = sharpenImg - imgLaplance;
resultImg.convertTo(resultImg, CV_8UC3);
imgLaplance.convertTo(imgLaplance, CV_8UC3);
imshow("sharpen image", resultImg);
// 3.convert to binary二值化
Mat binaryImg;
cvtColor(src, resultImg, CV_BGR2GRAY);
threshold(resultImg, binaryImg, 40, 255, THRESH_BINARY | THRESH_OTSU);
imshow("binary image", binaryImg);
//4.distance transform
Mat distImg;
distanceTransform(binaryImg, distImg, DIST_L1, 3, 5);
normalize(distImg, distImg, 0, 1, NORM_MINMAX);
imshow("distance result", distImg);
// 5.binary again 使用阈值,再次二值化,得到标记
threshold(distImg, distImg, .4, 1, THRESH_BINARY);//此时显示比较丑
Mat k1 = Mat::ones(13, 13, CV_8UC1);
erode(distImg, distImg, k1, Point(-1, -1));//腐蚀得到每个Peak - erode
imshow("distance binary image", distImg);
// 6.markers 编号处理(发现轮廓)标记
Mat dist_8u;
distImg.convertTo(dist_8u, CV_8U);
vector<vector<Point>> contours;
findContours(dist_8u, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));
// 7.create makers
Mat markers = Mat::zeros(src.size(), CV_32SC1);
for (size_t i = 0; i < contours.size(); i++)
{
drawContours(markers, contours, static_cast<int>(i), Scalar::all(static_cast<int>(i) + 1), -1);
}
circle(markers, Point(5, 5), 3, Scalar(255, 255, 255), -1);
imshow("my markers", markers * 1000);//markers灰度级别很低
// 8.perform watershed 分水岭变换
watershed(src, markers);
Mat mark = Mat::zeros(markers.size(), CV_8UC1);
markers.convertTo(mark, CV_8UC1);
bitwise_not(mark, mark, Mat());//反变换(取反)
imshow("watershed image", mark);
// 9.generate random color随机颜色
vector<Vec3b> colors;
for (size_t i = 0; i < contours.size(); i++)
{
int r = theRNG().uniform(0, 255);
int g = theRNG().uniform(0, 255);
int b = theRNG().uniform(0, 255);
colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
}
// 10.fill with color and display final result
Mat dst = Mat::zeros(markers.size(), CV_8UC3);
for (int row = 0; row < markers.rows; row++)
{
for (int col = 0; col < markers.cols; col++)
{
int index = markers.at<int>(row, col);
if (index > 0 && index <= static_cast<int>(contours.size()))
{
dst.at<Vec3b>(row, col) = colors[index - 1];
}
else
{
dst.at<Vec3b>(row, col) = Vec3b(0, 0, 0);
}
}
}
imshow("Final Result", dst);
}
完成代码下载:
链接:https://pan.baidu.com/s/1TfYM3hhI5e7BlpLxDpzmHA
提取码:x3r7