前言:跟着浅墨大神,学习OpenCV3后,笔记如下。
1 基础知识
1.1 加载、修改、保存图像
- 加载 cv::imread
- 保存 cv::cvtColor
- 保存 cv::imwrite
Mat image = imread("E:/result/MyPic1.png", 0); //路径,0代表灰度打开
imshow("image1", image);
Mat gray_image;
cvtColor(image, gray_image, COLOR_BGR2GRAY); //转换色彩空间
imshow("out_gray_image", gray_image);
imwrite("E:/result/Mypic1_opencv.jpg", gray_image); //保存图片
1.2 获取图像像素指针
void showCamera()
{
VideoCapture capture(0); //从摄像头中读取视频
while (true)
{
Mat frame;
capture >> frame;
namedWindow("读取视频");
imshow("读取视频", frame);
waitKey(30);
}
}
void showCapture()
{
VideoCapture capture;
capture.open("E:/result/MyVid.avi");
while (true)
{
Mat frame;
capture >> frame; //读取当前帧
if (frame.empty())
{
break;
}
imshow("读取视频", frame);
waitKey(30); //30ms
}
}
void showImage()
{
Mat src, dst;
src = imread("E:/result/MyPic1.png");
//src = imread("E:/result/zkc2.jpg");
if (!src.data)
{
cout << "could not open this image..." << endl;
return;
}
imshow("【原图】input image", src);
Mat edge, grayImage;
cvtColor(src, grayImage, CV_BGR2GRAY);
blur(grayImage, edge, Size(3, 3));
Canny(edge, edge, 3, 9, 3);
imshow("【灰度】", grayImage);
imshow("【边缘检测】Canny", edge);
}
1.3 创建滑动条
createTrackbar()
int createTrackbar(conststring& trackbarname, conststring& winname, int* value, int count, TrackbarCallback onChange=0,void* userdata=0);
【示例】
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
//-----------------------------------【全局函数声明部分】-----------------
// 描述:全局函数声明
//--------------------------------------------------------------------
Mat img;
int threshval = 160; //轨迹条滑块对应的值,给初值160
//-----------------------------【on_trackbar( )函数】-------------------
// 描述:轨迹条的回调函数
//--------------------------------------------------------------------
static void on_trackbar(int, void*)
{
Mat bw = threshval < 128 ? (img < threshval) : (img > threshval);
//定义点和向量
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
//查找轮廓
findContours( bw, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
//初始化dst
Mat dst = Mat::zeros(img.size(), CV_8UC3);
//开始处理
if( !contours.empty() && !hierarchy.empty() )
{
//遍历所有顶层轮廓,随机生成颜色值绘制给各连接组成部分
int idx = 0;
for( ; idx >= 0; idx = hierarchy[idx][0] )
{
Scalar color( (rand()&255), (rand()&255), (rand()&255) );
//绘制填充轮廓
drawContours( dst, contours, idx, color, CV_FILLED, 8, hierarchy );
}
}
//显示窗口
imshow( "Connected Components", dst );
}
//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main( )
{
system("color 5F");
//载入图片
img = imread("1.jpg", 0);
if( !img.data ) { printf("Oh,no,读取img图片文件错误~! \n"); return -1; }
//显示原图
namedWindow( "Image", 1 );
imshow( "Image", img );
//创建处理窗口
namedWindow( "Connected Components", 1 );
//创建轨迹条
createTrackbar( "Threshold", "Connected Components", &threshval, 255, on_trackbar );
on_trackbar(threshval, 0);//轨迹条回调函数
waitKey(0);
return 0;
}
2 常用的数据结构和函数
2.1 点:Point类
//二维坐标系下点
Point point;
point.x = 10;
point.y = 8;
//或者
Point point = Point(10, 8);
2.2 颜色:Scalar类
//Scalar()具有4个元素的数组,最后一个可以不用,前三个表示BGR
Scalar(a, b, c); //a->B b->G c->R
2.3 尺寸:Size类
Size s1 = Size(5, 5); //构造Size宽5,高5,即xxx.width 和 xxx.height 均5
2.4 矩形:Rect类
//Rect包含左上角点坐标x,y 矩形的宽度width和高度height
//contains(Point) 判断点是否在内
//inside(Rect) 判断矩形是否在内
//tl()返回左上角坐标;
//br()返回右下角坐标;
Rect rect = rect1 & rect2; //交集
Rect rect = rect1 | rect2; //并集
Rect rectShift = rect + point; //平移
Rect rectScale = rect + size; //缩放
2.5 颜色空间转换:cvtColor()函数
//函数原型 输入图,输出图,颜色转换标识符,通道数(默认保持不变)
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0);
cvtColor(srcImage, dstImage, COLOR_BGR2GRAY); //转换为灰度图
2.6 其他常用的知识点
Matx轻量级的Mat
Vec是Matx派生类,一维的
Range
Matx23f image1; //2*3float类型的Matx
Range:all(); //其实就是MATLAB里的符号
Range(a, b); //就是MATLAB中的a:b
OpenCV中防止溢出的函数
alignPtr、alignSize、allocate、deallocate、fastMalloc、fastFree
include <math.h>
计算向量角度函数 fastAtan2
计算立方根函数 cubeRoot
向上取整函数 cvCeil
向下取整函数 cvFloor
四舍五入函数 cvRound
判断自变量是否无穷大 cvIsInf
判断自变量是否不是一个数 cvIsNaN
显示文字函数 getTextSize、cvInitFont、putText
作图相关函数 circle、clipLine、ellipse、ellipse2Poly、line、rectangle、polylines、类LineIterator
填充相关函数 fillConvexPoly、fillPoly
OpenCv中RNG()函数作用为初始化随机数状态生成器
3 基本图形的绘制
- 绘制直线 Line 函数
- 绘制椭圆 ellipse 函数
- 绘制矩形 rectangle 函数
- 绘制圆 circle 函数
- 绘制填充的多边形 fillPoly 函数
void DrawEllopse(Mat img, double angele); //绘制椭圆
void DrawFilledCircle(Mat img, Point center); //绘制实心圆
void DrawPolygon(Mat img); //绘制凸多边形
void DrawLine(Mat img, Point start, Point end); //绘制直线
3.1 DrawEllipse()函数的写法
//绘制椭圆
void DrawEllipse(Mat img, double angle)
{
int thickness = 2;
int lineType = 8;
ellipse(img,
Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2), //椭圆的中心点坐标
Size(WINDOW_WIDTH / 4, WINDOW_WIDTH / 16), //在这个大小的矩形内
angle, //椭圆旋转角度
0,
360,
Scalar(255, 129, 0), //BGR蓝色
thickness, //线宽2
lineType //线型8,联通线型
);
}
3.2 DrawFilledCircle()函数的写法
void DrawFilledCircle(Mat img, Point center)
{
int thinkness = -1;
int lineType = 8;
circle(img,
center, //圆心
WINDOW_WIDTH / 32, //半径
Scalar(0, 0, 255), //颜色
thinkness, //线宽-1,故为实心圆
lineType);
}
3.3 DrawPolygo()函数的写法
void DrawPolygon(Mat img)
{
int lineType = 8;
//Creat points
Point rookPoints[1][20];
rookPoints[0][0] = Point(WINDOW_WIDTH/4, 7* WINDOW_WIDTH/8);
rookPoints[0][1] = Point(3*WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8);
rookPoints[0][2] = Point(3*WINDOW_WIDTH / 4, 13 * WINDOW_WIDTH / 16);
rookPoints[0][3] = Point(11*WINDOW_WIDTH / 16, 13 * WINDOW_WIDTH / 16);
rookPoints[0][4] = Point(19*WINDOW_WIDTH / 32, 3 * WINDOW_WIDTH / 8);
rookPoints[0][5] = Point(3*WINDOW_WIDTH / 4, 3 * WINDOW_WIDTH / 8);
rookPoints[0][6] = Point(3*WINDOW_WIDTH / 4, WINDOW_WIDTH / 8);
rookPoints[0][7] = Point(26*WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
rookPoints[0][8] = Point(26*WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
rookPoints[0][9] = Point(22*WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
rookPoints[0][10] = Point(22*WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
rookPoints[0][11] = Point(18*WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
rookPoints[0][12] = Point(18*WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
rookPoints[0][13] = Point(14*WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
rookPoints[0][14] = Point(14*WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
rookPoints[0][15] = Point(WINDOW_WIDTH / 4, WINDOW_WIDTH / 8);
rookPoints[0][16] = Point(WINDOW_WIDTH / 4, 3 * WINDOW_WIDTH / 8);
rookPoints[0][17] = Point(13*WINDOW_WIDTH / 32, 3 * WINDOW_WIDTH / 8);
rookPoints[0][18] = Point(5*WINDOW_WIDTH / 16, 13 * WINDOW_WIDTH / 16);
rookPoints[0][19] = Point(WINDOW_WIDTH / 4, 13 * WINDOW_WIDTH / 16);
const Point* ppt[1] = { rookPoints[0] };
int npt[] = { 20 };
fillPoly(img,
ppt, //多边形顶点集
npt, //多边形顶点数目
1, //绘制的数量
Scalar(255, 255, 255), //颜色:白色
lineType);
}
3.4 DrawLine()函数的绘制
void DrawLine(Mat img, Point start, Point end)
{
int thinckness = 2;
int lineType = 8;
line(img,
start,
end,
Scalar(0, 0, 0),
thinckness,
lineType);
}
3.5 main()
#include <iostream>
#include <cstdlib>
#include <opencv2/opencv.hpp>
#define WINDOW_WIDTH 600
#define WINDOW_NAME1 "【绘制图1】"
#define WINDOW_NAME2 "【绘制图2】"
using namespace std;
using namespace cv;
void DrawEllipse(Mat img, double angele); //绘制椭圆
void DrawFilledCircle(Mat img, Point center); //绘制实心圆
void DrawPolygon(Mat img); //绘制凸多边形
void DrawLine(Mat img, Point start, Point end); //绘制直线
int main()
{
Mat atomImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);
Mat rookImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);
//1.1 绘制椭圆
DrawEllipse(atomImage, 90);
DrawEllipse(atomImage, 0);
DrawEllipse(atomImage, 45);
DrawEllipse(atomImage, -45);
//1.2 绘制圆心
DrawFilledCircle(atomImage, Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2));
//2.1 绘制多边形
DrawPolygon(rookImage);
//2.2 绘制矩形
rectangle(rookImage,
Point(0, 7*WINDOW_WIDTH / 8),
Point(WINDOW_WIDTH, WINDOW_WIDTH),
Scalar(0, 255, 255),
-1,
8);
//2.3绘制一些线段
DrawLine(rookImage, Point(0, 15*WINDOW_WIDTH / 16),
Point(WINDOW_WIDTH, 15* WINDOW_WIDTH / 16));
DrawLine(rookImage, Point(WINDOW_WIDTH / 4, 7*WINDOW_WIDTH / 8),
Point(WINDOW_WIDTH / 4, WINDOW_WIDTH));
DrawLine(rookImage, Point(WINDOW_WIDTH / 2, 7*WINDOW_WIDTH / 8),
Point(WINDOW_WIDTH / 2, WINDOW_WIDTH));
DrawLine(rookImage, Point(3*WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8),
Point(3*WINDOW_WIDTH / 4, WINDOW_WIDTH));
//3.1 showImage
imshow(WINDOW_NAME1, atomImage);
moveWindow(WINDOW_NAME1, 0, 200);
imshow(WINDOW_NAME2, rookImage);
moveWindow(WINDOW_NAME2, WINDOW_WIDTH, 200);
waitKey();
return 0;
}
4 croe组件进阶
- 感兴趣区域(ROI)
- 图像混合
- 分离颜色通道
- 多通道颜色混合
- 调整对比度和亮度值
- 图像傅立叶变换
- 输入输出XML和YAML文件
4.1 图像中的像素
4.1.1 颜色空间缩减
//0~9 ->0;10~19->10
int divideWith = 10;
uchar table[256];
for(int i = 0; i < 256; i++)
{
table[i] = divideWith * (i / divideWith);
}
4.1.2 LUT函数:Look up table 操作
//建立Mat性用于查表
Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.data;
for (int i = 0; i < 256; i++)
p[i] = table[i];
//调用函数(I是输入,J是输出)
for (int i = 0; i < times; i++)
LUT(I, lookUpTable, J);
4.1.3 计时函数
double time0 = static_cast<double>(getTickCount()); //记录起始时间
// **** 图像操作 *** //
time0 = ((double)getTickCount() - time0) / getTickFrequency();
cout << "图像操作运行时间为:" << time0 << "秒" << endl; //输出运行时间
4.1.4 访问图像中像素的三种方法
- 指针访问
- 迭代器 iterator
- 动态地址计算
主程序 main
把256种颜色缩减成64种颜色
#include <iostream>
#include <cstdlib>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp> //模块
#include <opencv2/imgproc/imgproc.hpp> //图像处理头文件
using namespace std;
using namespace cv;
void colorReduce(Mat & inputImage, Mat & outputImage, int div);
int main()
{
//1.创建原图并显示
Mat srcImage = imread("E:/result/MyPic1.png");
imshow("原始图像", srcImage);
//2.按照原图参数创建效果图
Mat dstImage;
dstImage.create(srcImage.rows, srcImage.cols, srcImage.type());
//3.记录起始时间
double time0 = static_cast<double>(getTickCount());
//4.调用颜色空间缩减函数
colorReduce(srcImage, dstImage, 32);
//5.计算运行时间并输出
time0 = ((double)getTickCount() - time0) / getTickFrequency();
cout << "图像操作运行时间为:" << time0 << "秒" << endl;
//6.显示效果图
imshow("效果图", dstImage);
waitKey();
return 0;
}
1.指针访问
void colorReduce(Mat & inputImage, Mat & outputImage, int div)
{
outputImage = inputImage.clone(); //复制实参到临时变量
int rowNumber = outputImage.rows; //行数
int colNumber = outputImage.cols * outputImage.channels(); //列数*通道数 = 每一行元素个数
//双循环,遍历所有像素值
for (int i = 0; i < rowNumber; i++) //行循环
{
uchar * data = outputImage.ptr<uchar>(i); //获取第i行的首地址
for (int j = 0; j < colNumber; j++) //列循环
{
data[j] = data[j] / div * div + div / 2;
}
}
}
2.迭代器访问
void colorReduce(Mat & inputImage, Mat & outputImage, int div)
{
outputImage = inputImage.clone();
//获取迭代器
Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>(); //初始位置的迭代器
Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>(); //终止位置的迭代器
//存取彩色图像像素
for (; it != itend; it++)
{
//处理每个像素
(*it)[0] = (*it)[0] / div * div + div / 2;
(*it)[1] = (*it)[1] / div * div + div / 2;
(*it)[2] = (*it)[2] / div * div + div / 2;
}
}
3 动态地址
void colorReduce(Mat & inputImage, Mat & outputImage, int div)
{
outputImage = inputImage.clone();
int rowNumber = outputImage.rows;
int colNumber = outputImage.cols;
//存取彩色图像像素
for (int i = 0; i < rowNumber; i++)
{
for (int j = 0; j < colNumber; j++)
{
outputImage.at<Vec3b>(i, j)[0] =
outputImage.at<Vec3b>(i, j)[0] / div * div + div / 2; //蓝色通道
outputImage.at<Vec3b>(i, j)[1] =
outputImage.at<Vec3b>(i, j)[1] / div * div + div / 2; //绿色通道
outputImage.at<Vec3b>(i, j)[2] =
outputImage.at<Vec3b>(i, j)[2] / div * div + div / 2; //红色通道
}
}
}
4.2 ROI区域图像叠加&图像混合
4.2.1 感兴趣区域:ROI (region of interesting)
Mat srcImage = imread...
Mat logo = imread...
Mat imageROI = srcImage(Rect(500, 250, logo.cols, logo.rows)); //img的ROI
Mat mask = imread("E:/pic.png", 0); //必须灰度图
logo.copyTo(imageROI, mask);
4.2.2 线性混合操作
计算数组加权和addWeighted()函数
void addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype = -1);
//输入1,权重1,输入2(与1类型同),权重2,gamma,输出,默认深度同。
//内部操作如下
dst = src1[I]*alpha + src2[I]*beta + gamma;
4.3 分离颜色通道、多通道图像混合
- split
- merge
4.3.1 通道分离:split()函数
void split(const Mat& src, Mat* mvbegin);
void split(InputArray m, OutputArrayOfArrays mv);
基础示例
vector<Mat> channels;
Mat imageBlueChannel, imageGreenChannel, imageRedChannel;
srcImage = imread("MyPic");
//三通道图像转换成3个单通道图像
split(srcImage, channels); //分离颜色通道
imageBlueChannel = channels.at(0);
imageGreenChannel = channels.at(1);
imageRedChannel = channels.at(2);
4.3.2 通道合并:merge()函数
void merge(const Mat& mv, size_tcont, OutputArray dst);
void merge(InputArrayOfArrays mv, OutputArray dst);
Mat srcImage = imread("1.png");
Mat imageBlueChannel, imageGreenChannel, imageRedChannel, mergeImage;
//三通道图像转换成3个单通道图像
split(srcImage, channels); //分离颜色通道
imageBlueChannel = channels.at(0);
imageGreenChannel = channels.at(1);
imageRedChannel = channels.at(2);
//对拆分的通道进行合并
merge(channels, mergeImage);
4.4 图像对比度、亮度调整
4.4.1 理论依据
访问BGR像素
g(x) = a*f(x) + b
for (int y = 0; y < image.rows; y++)
{
for (int x = 0; x < image.cols; x++)
{
for (int c = 0; c < 3; c++)
{
new_image.at<Vec3b>(y, x)[c] =
saturate_cast<uchar>((g_nContrastValue*0.01)*(image.at<Vec3b>(y, x)[c])
+ g_nBrightValue);
}
}
}
//saturate_cast 保护防止溢出
4.4.2 示例程序:图像对比度、亮度调整
#include <iostream>
#include <cstdlib>
#include <math.h>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
static void on_ContrastAndBright(int, void *); //改变图像对比度和亮度值的回调函数
//static void ShowHelpText();
int g_nContrastValue; //对比度
int g_nBrightValue; //亮度值
Mat g_srcImage, g_dstImage;
int main()
{
//【1】读取输入图像
g_srcImage = imread("E:/result/MyPic.png");
if (!g_srcImage.data) { cout << "读取g_srcImage error~"; return false; }
g_dstImage = Mat::zeros(g_srcImage.size(), g_srcImage.type());
//【2】设置对比度和亮度
g_nBrightValue = 80;
g_nContrastValue = 80;
//【3】创建效果图窗口
namedWindow("【效果图窗口】", 1);
//【4】创建轨迹条
createTrackbar("对比度", "【效果图窗口】", &g_nContrastValue,
300, on_ContrastAndBright);
createTrackbar("亮 度", "【效果图窗口】", &g_nBrightValue,
200, on_ContrastAndBright);
//【5】进行回调函数初始化
on_ContrastAndBright(g_nContrastValue, 0);
on_ContrastAndBright(g_nBrightValue, 0);
//【6】按下"q",程序退出
while(char(waitKey(1)) != 'q'){}
destroyAllWindows();
return 0;
}
static void on_ContrastAndBright(int, void *)
{
cvNamedWindow("【原图窗口】", 1);
//for计算:g_dstImage(i, j) = a*g_dstImage(i, j) + b
for (int y = 0; y < g_srcImage.rows; y++)
{
for (int x = 0; x < g_srcImage.cols; x++)
{
for (int c = 0; c < 3; c++)
{
g_dstImage.at<Vec3b>(y, x)[c] =
saturate_cast<uchar>((g_nContrastValue*0.01)*(g_srcImage.at<Vec3b>(y, x)[c])
+ g_nBrightValue);
}
}
}
imshow("【原图窗口】", g_srcImage);
imshow("【效果图窗口】", g_dstImage);
}
4.5 离散傅立叶变换
- 图像增强和去噪
- 图像分割
- 边缘检测、特征提取、图像压缩等
4.5.1 dft()函数
对一维或二维浮点数进行正向和反向离散傅立叶变换
void dft(InputArray src, OutputArray dst, int flages = 0, int nonzeroRows = 0)
//参数3->变换类型(默认正变换);参数4->最好是要处理的行C.rows
dft()函数计算两个二维实矩阵卷积的示例核心片段
void convolveDFT(InputArray A, InputArray B, OutputArray C)
{
//【1】初始化输出矩阵
C.create(abs(A.rows - B.rows) + 1, abs(A.cols - B.cols) + 1, A.type());
Size dftSize;
//【2】计算DFT变换的尺寸
dftSize.width = getOptimalDFTSize(A.cols + B.cols - 1);
dftSize.height = getOptimalDFTSize(A.rows + B.rows - 1);
//【3】分配临时缓冲区并初始化为零
Mat tempA(dftSize, A.type(), Scalar::all(0));
Mat tempB(dftSize, B.type(), Scalar::all(0));
//【4】分别复制A和B到tempA和tempB的左上角
Mat roiA(tempA, Rect(0, 0, A.cols, A.rows));
A.copyTo(roiA);
Mat roiB(tempB, Rect(0, 0, B.cols, B.rows));
B.copyTo(roiB);
//【5】就地操作(in-place),快速傅立叶变换,并将nonzeroRows参数置为非零
dft(tempA, tempA, 0, A.rows);
dft(tempB, tempB, 0, B.rows);
//【6】将得到的频谱相乘,结果放在tempA中
mulSpectrums(tempA, tempB, tempA, 0);
//【7】将结果变换为频域,且结果行(result rows)都为非零
//我们只需要其中的C.rows的第一行,所以采用nonzeroRows == C.rows
dft(tempA, tempA, DFT_INVERSE + DFT_SCALE, C.rows);
//【8】将结果复制到C中
tempA(Rect(0, 0, C.cols, C.rows)).copyTo(C);
//所有的临时缓冲区将被自动释放,无需收尾工作
}
4.5.2 返回DFT最优尺寸大小:getOptimalDEFSize()函数
返回给定向量尺寸的傅立叶最优尺寸大小
int 类型的 vecsize, 向量尺寸,即图片的rows、cols
int getOptimalDEFSize(int vecsize);
4.5.3 扩充图像边界:copyMakeBorder()函数
void copyMakeBorder(InputArray src, OutputArray dst, int top, int bottom, int left, int right, int borderType, const Scalar& value = Scalar());
- 参数1:源图
- 参数2:输出,size为Size(src.cols+left+right, src.rows+top+bottom)
- 参数3-6:扩充多少像素,例如top=2,…
- 参数7:边界类型,常用BORDER_CONSTANT
- 参数8:默认为0
4.5.4 计算二维矢量的幅值:magnitude()函数
void magnitude(InputArray x, InputArray y, OutputArray magnitude
);
- 浮点型X坐标,实部
- 浮点型Y坐标,虚部
- 输出
4.5.5 计算自然对数:log()函数
void log(InputArray src, OutputArray dst);
4.5.6 矩阵归一化:normalize()函数
void normalize(InputArray src, OutputArray dst, double alpha = 1,
double beta = 0, int norm_type = NORM_L2, int dtype = -1, InputArray mask = noArray())
- 参数3:归一化后的最大值,默认为1
- 参数4:归一化后的最大值,默认为0
- 参数5:归一化后的类型 NORM_LNF、NORM_L1、NORM_L2、NORM_MINMAX
- 参数6:默认-1,输出矩阵和src有同样类型
- 参数7:可选择掩膜mask操作
5 图像处理:线性&非线性滤波
imgproc组件是Image和Process组合
- 方框滤波——boxblur函数
- 均值滤波(邻域平均滤波)——blur函数
- 高斯滤波——GaussianBlur函数
- 中值滤波——medianBlur函数
- 双边滤波——bilateralFilter函数
5.1 方框滤波(box Filter)
方框滤波(box Filter)被封装在一个名为boxblur的函数中,作用是模糊一张图片
void boxFilter(InputArray src,OutputArray dst, int ddepth, Size ksize, Point anchor=Point(-1,-1), boolnormalize=true, int borderType=BORDER_DEFAULT)
均值滤波是方框滤波归一化后的特殊情况。
如果我们要在可变的窗口中计算像素总和,可以使用integral()函数。
5.2 均值滤波 (blur)
破坏了细节
void blur(InputArray src, OutputArraydst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )
第三个参数,Size类型(对Size类型稍后有讲解)的ksize,内核的大小。一般这样写Size( w,h )来表示内核的大小( 其中,w 为像素宽度, h为像素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小。
第四个参数:默认中心点为目标点。
5.3 高斯滤波(GaussianBlur)
void GaussianBlur(InputArray src,OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, intborderType=BORDER_DEFAULT )
5.4 中值滤波:medianBlur()
medianBlur( InputArray src,OutputArray dst, int ksize )
5.5 双边滤波:bilateralFilter()
void bilateralFilter(InputArray src, OutputArraydst, int d, double sigmaColor, double sigmaSpace, int borderType=BORDER_DEFAULT)
//载入原图
Mat image=imread("1.jpg");
//进行双边滤波操作
Mat out;
bilateralFilter( image, out, 25, 25*2, 25/2 );
5.6 【示例】双边滤波+bar
Mat g_srcImage, g_dstImage;
//双边滤波参数值
int g_nBilateralFilterValue = 10;
//轨迹条回调函数 -
static void on_BilateralFilter(int, void *);
int main()
{
g_srcImage = imread("E:/result/MyPic.png", 1);
if (!g_srcImage.data)
{
cout << "读取错误" << endl;
return 0;
}
//复制原图
g_dstImage = g_srcImage.clone();
namedWindow("【原图】双边滤波", 1);
imshow("【原图】双边滤波", g_srcImage);
//双边滤波操作
namedWindow("【效果图】双边滤波", 1);
createTrackbar("value:", "【效果图】双边滤波", &g_nBilateralFilterValue,
50, on_BilateralFilter);
on_BilateralFilter(g_nBilateralFilterValue, 0);
waitKey(0);
return 0;
}
//回调函数
static void on_BilateralFilter(int, void *)
{
bilateralFilter(g_srcImage, g_dstImage, g_nBilateralFilterValue,
g_nBilateralFilterValue * 2, g_nBilateralFilterValue / 2);
imshow("【效果图】双边滤波", g_dstImage);
}
6 图像处理:形态学滤波(1)
- 腐蚀
- 膨胀
腐蚀和膨胀是对***白色部分(高亮部分***)而言的,不是黑色部分。
膨胀就是图像中的高亮部分进行膨胀,“领域扩张”,效果图拥有比原图更大的高亮区域。腐蚀就是原图中的高亮部分被腐蚀,“领域被蚕食”,效果图拥有比原图更小的高亮区域。
6.1 膨胀:dilate()
void dilate(
InputArray src,
OutputArray dst,
InputArray kernel,
Point anchor=Point(-1,-1),
int iterations=1,
int borderType=BORDER_CONSTANT,
const Scalar& borderValue=morphologyDefaultBorderValue()
);
第三个参数,InputArray类型的kernel,膨胀操作的核。若为NULL时,表示的是使用参考点位于中心3x3的核。
我们一般使用函数 getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)。
其中,getStructuringElement函数的第一个参数表示内核的形状,可以选择如下三种形状之一:
- 矩形: MORPH_RECT
- 交叉形: MORPH_CROSS
- 椭圆形: MORPH_ELLIPSE
而getStructuringElement函数的第二和第三个参数分别是内核的尺寸以及锚点的位置。
我们一般在调用erode以及dilate函数之前,先定义一个Mat类型的变量来获得getStructuringElement函数的返回值。
对于锚点的位置,有默认值Point(-1,-1),表示锚点位于中心。且需要注意,十字形的element形状唯一依赖于锚点的位置。而在其他情况下,锚点只是影响了形态学运算结果的偏移。
getStructuringElement函数相关的调用示例代码如下:
int g_nStructElementSize = 3; //结构元素(内核矩阵)的尺寸
//获取自定义核
Mat element = getStructuringElement(MORPH_RECT,
Size(2*g_nStructElementSize+1,2*g_nStructElementSize+1),
Point( g_nStructElementSize, g_nStructElementSize ));
【膨胀核心代码】
//载入原图
Mat image = imread("1.jpg");
//获取自定义核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
Mat out;
//进行膨胀操作
dilate(image, out, element);
6.2 腐蚀:erode()
参数列表与6.1相同
【腐蚀核心代码】
//载入原图
Mat image = imread("1.jpg");
//获取自定义核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
Mat out;
//进行腐蚀操作
erode(image,out, element);
7 图像处理:形态学滤波(2)
7.1 开运算:Opening Operation
先腐蚀后膨胀的过程
开运算可以用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积。
7.2 闭运算:Closing Operation
先膨胀后腐蚀的过程
排除小型黑洞(黑色区域)
7.3 形态学梯度:MorphologicalGradient
膨胀图与腐蚀图之差
对二值图像进行这一操作可以将团块(blob)的边缘突出出来
7.4 顶帽:Top Hat
原图像与“开运算“的结果图之差
顶帽运算往往用来分离比邻近点亮一些的斑块。当一幅图像具有大幅的背景的时候,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。
7.5 黑帽:Black Hat
”闭运算“的结果图与原图像之差
分离比邻近点暗一些的斑块
7.6 核心API函数:morphologyEx()
void morphologyEx(
InputArray src,
OutputArray dst,
int op,
InputArraykernel,
Pointanchor=Point(-1,-1),
intiterations=1,
intborderType=BORDER_CONSTANT,
constScalar& borderValue=morphologyDefaultBorderValue() );
MORPH_OPEN – 开运算(Opening operation)
MORPH_CLOSE – 闭运算(Closing operation)
MORPH_GRADIENT -形态学梯度(Morphological gradient)
MORPH_TOPHAT - “顶帽”(“Top hat”)
MORPH_BLACKHAT - “黑帽”(“Black hat“)
MORPH_ERODE - 腐蚀
MORPH_DILATE - 膨胀
getS参数一:
- 矩形: MORPH_RECT
- 交叉形: MORPH_CROSS
- 椭圆形: MORPH_ELLIPSE
【getS示例】
int g_nStructElementSize = 3; //结构元素(内核矩阵)的尺寸
//获取自定义核
Mat element =getStructuringElement(MORPH_RECT,
Size(2*g_nStructElementSize+1,2*g_nStructElementSize+1),
Point(g_nStructElementSize, g_nStructElementSize ));
//定义核[常用版本]
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
8 图像处理:一些算法
8.1 漫水填充算法:floodFill()
用特定的颜色填充联通区域
标记或分离图像的一部分以便对其进行进一步处理或分析
int floodFill(InputOutputArray image, InputOutputArray mask, Point seedPoint,Scalar newVal, Rect* rect=0, Scalar loDiff=Scalar(), Scalar upDiff=Scalar(), int flags=4 )
8.2 图像金字塔与图像尺寸缩放
* **resize() 缩放**
* **pyrUp() 向上采样(高斯金字塔) -- 尺寸加倍**
* **pyrDown() 向下采样(拉普拉斯金字塔)**
resize() 函数
void resize(InputArray src,OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR )
INTER_NEAREST - 最近邻插值
INTER_LINEAR - 线性插值(默认值)
INTER_AREA - 区域插值(利用像素区域关系的重采样插值)
INTER_CUBIC –三次样条插值(超过4×4像素邻域内的双三次插值)
INTER_LANCZOS4 -Lanczos插值(超过8×8像素邻域的Lanczos插值)
若要缩小图像,一般情况下最好用CV_INTER_AREA来插值,
而若要放大图像,一般情况下最好用INTER_CUBIC(效率不高,慢,不推荐使用)或INTER_LINEAR(效率较高,速度较快,推荐使用)。
【方式一:设定目标尺寸】
Mat dst=Mat::zeros(512 ,512, CV_8UC3 );//新建一张512x512尺寸的图片
Mat src=imread(“1.jpg”);
//显式指定dsize=dst.size(),那么fx和fy会其计算出来,不用额外指定。
resize(src, dst, dst.size());
【方式二:设定放缩因子】
Mat dst;
Mat src=imread(“1.jpg”)
//指定fx和fy,让函数计算出目标图像的大小。
resize(src, dst, Size(), 0.5, 0.5); //x,y 缩放0.5
pyrUp() 向上采样(高斯金字塔) – 尺寸放大
void pyrUp(InputArray src, OutputArraydst, const Size& dstsize=Size(), int borderType=BORDER_DEFAULT )
pyrDown() 向下采样(拉普拉斯金字塔) – 尺寸缩小
void pyrDown(InputArray src,OutputArray dst, const Size& dstsize=Size(), int borderType=BORDER_DEFAULT)
8.3 阈值化
根据灰度差异,选区出目标
- Threshold() 固定阈值操作
- adaptiveThreshold() 自适应阈值
Threshold() 固定阈值操作
典型应用于灰度图处理,得到二值图像
double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type)
参数三:具体的阈值。
adaptiveThreshold() 自适应阈值
void adaptThresholde(InputArray src, OutputArray dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C)
9 图像变换:边缘检测
* **Canny 算子**
* **Sobel 算子**
* **Laplacian 算子**
* **Scharr 滤波器**
9.1 Canny() 函数
void Canny(InputArray image,OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false )
//[示例]
Mat src = imread("1.jpg"); //工程目录下应该有一张名为1.jpg的素材图
Canny(src, src, 3, 9, 3);
imshow("【效果图】Canny边缘检测", src);
9.2 sobel 算子
Sobel 算子是一个主要用作边缘检测的离散微分算子 (discrete differentiation operator)。 Sobel算子结合了高斯平滑和微分求导,用来计算图像灰度函数的近似梯度。
在图像的任何一点使用此算子,将会产生对应的梯度矢量或是其法矢量。
void Sobel (
InputArray src,//输入图
OutputArray dst,//输出图
int ddepth,//输出图像的深度
int dx,
int dy,
int ksize=3,
double scale=1,
double delta=0,
int borderType=BORDER_DEFAULT );
9.3 Laplacian() 函数
void Laplacian(InputArray src,OutputArray dst, int ddepth, int ksize=1, double scale=1, double delta=0, intborderType=BORDER_DEFAULT );
9.4 scharr 滤波器
使用Scharr滤波器运算符计算x或y方向的图像差分。
其实它的参数变量和Sobel基本上是一样的,除了没有ksize核的大小。
void Scharr(
InputArray src, //源图
OutputArray dst, //目标图
int ddepth,//图像深度
int dx,// x方向上的差分阶数
int dy,//y方向上的差分阶数
double scale=1,//缩放因子
double delta=0,// delta值
intborderType=BORDER_DEFAULT )// 边界模式
10 图像变换:霍夫变换
10.1 霍夫线变换
<1>标准霍夫变换(StandardHough Transform,SHT),由HoughLines函数调用。
<2>多尺度霍夫变换(Multi-ScaleHough Transform,MSHT),由HoughLines函数调用。
<3>累计概率霍夫变换(ProgressiveProbabilistic Hough Transform,PPHT),由HoughLinesP函数调用。
10.2 标准霍夫变换:HoughLines()
void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )
10.3 累计概率霍夫变换:HoughLinesP()
void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0 )
10.4 霍夫圆变换:HoughCircles()
oughCircles函数可以利用霍夫变换算法检测出灰度图中的圆
void HoughCircles(InputArray image,OutputArray circles, int method, double dp, double minDist, double param1=100,double param2=100, int minRadius=0, int maxRadius=0 )
11 图像变换:其他知识
11.1 重映射:remap()
镜像、翻转
11.2 仿射变换
* **旋转 rotation(线性变换)**
* **平移 translation(向量加)**
* **缩放 scale(线性变换)**
11.3 仿射变换:WarpAffine()函数
11.4 计算二维旋转变换矩阵:getRotationMatrix2D()函数
11.5 直方图均衡化:equalizeHist()
void equalizeHist(InputArray src, OutputArray dst)
12 图像轮廓与图像分割修复
12.1 寻找轮廓:findContours()
用于二值图像中寻找轮廓
vector<vector<Point>> contours; //轮廓
vector<Vec4i> hierarchy; //层次结构
12.2 绘制轮廓:drawContours()
绘制外部/内部轮廓
12.3 寻找凸包:convexHull()
12.4 使用多边形轮廓包围
12.4.1 返回外部矩形边界:boundingRect()
//返回Rect
12.4.2 寻找最小包围矩形:minAreaRect()
//返回RotatedRect
12.4.3 寻找最小包围圆形:minEnclosingCircle()
12.4.4 椭圆拟合二维点集:fitEllipse()
//返回RotatedRect
12.4.5 逼近多边形曲线:approxPolyDP()
12.5 图像的矩
12.5.1 矩的计算:moments()
//返回Moments
12.5.2 轮廓面积:contourArea()
//返回double
12.5.3 轮廓长度:arcLength()
//返回double
12.6 分水岭算法:watershed()
12.7 图像修补:inpaint()
13 直方图与匹配
13.1 计算直方图:calcHist()
13.2 找寻最值:minMaxLoc()
13.3 对比直方图:compareHist()
13.4 计算反向投影:calcBackProject()
13.5 通道复制:mixChannels()
13.6 模板匹配:matchTemplate()
14 深入feature2d组件
14.1 harris角点检测:cornerHarris()
14.2 Shi-Tomasi角点检测:确定强角点:goodFeaturesToTrack()
14.3 寻找亚像素点:cornerSubPix()
15 特征检测与匹配
- FAST—FastFeatureDetector
- STAR—StarFeatureDetector
- SIFT—SIFT
- SURF—SURF
- ORB—ORB
- MSER—MSER
- GFTT—GoodFeatureToTrackDetector
- HARRIS—GoodFeatureToTrackDetector(配合Harris detector操作)
- Dense—DenseFeatureDetector
- SimpleBlob—SimpleBlobDetector
15.1 绘制关键点:drawKeyPoints()
16 xfeatures2d
16.1 SURF
#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <iostream>
using namespace std;
using namespace cv;
using namespace cv::xfeatures2d;
int main(int argc, char** argv)
{
Mat src = imread("1.jpg", IMREAD_GRAYSCALE);
if (src.empty()) { cout << "load error!" << endl; return -1; }
namedWindow("input image", WINDOW_AUTOSIZE);
imshow("input image", src);
//SURF特征检测
int minHessian = 400;
Ptr<SURF> detector = SURF::create(minHessian);
vector<KeyPoint> keypoints;
detector->detect(src, keypoints, Mat());
//绘制关键点
Mat keypoint_img;
drawKeypoints(src, keypoints, keypoint_img, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
imshow("keypoints", keypoint_img);
waitKey(0);
return 0;
}
16.2 SIFT
#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <iostream>
using namespace std;
using namespace cv;
using namespace cv::xfeatures2d;
int main(int argc, char** argv)
{
Mat src = imread("1.jpg", IMREAD_GRAYSCALE);
if (src.empty()) { cout << "load error!" << endl; return -1; }
namedWindow("input image", WINDOW_AUTOSIZE);
imshow("input image", src);
//SIF特征检测
int numFeatures = 100;
Ptr<SIFT> detector = SIFT::create(numFeatures);
vector<KeyPoint> keypoints;
detector->detect(src, keypoints, Mat());
//绘制
Mat keypoints_img;
drawKeypoints(src, keypoints, keypoints_img, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
namedWindow("SIFT - OUT", WINDOW_AUTOSIZE);
imshow("SIFT - OUT", keypoints_img);
waitKey(0);
return 0;
}
16.3 HOG
16.4 Haar
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oa46OTck-1582962531761)(https://uploader.shimo.im/f/bGxaBSZQO1oCcd8A.png!thumbnail)]
16.5 匹配
16.5.1 Burte-Force 暴力匹配
BFMacher matcher(NORM_L2);
16.5.2 FLANN 快速特征匹配
//FlannBasedMatcher matcher;
FlannBasedMatcher matcher(new flann::LshIndexParams(20, 10, 2));
16.5.3 对象的形变与位置变换
16.5.4 AKAZE局部特征提取
Ptr<AKAZE> detector = AKAZE::create();
16.5.5 BRISK
Ptr<Feature2D> detector = BRISK::create();
Ptr<Feature2D> detector = BRISK::create();
vector<KeyPoint> keypoints;
detector->detect(srcImage1, keypoints, Mat());
Mat resultImg;
drawKeypoints(srcImage1, keypoints, resultImg);
imshow("resultImg", resultImg);
16.6 级联检测器 - 人脸检测
#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <opencv2/features2d.hpp>
#include <iostream>
using namespace std;
using namespace cv;
using namespace cv::xfeatures2d;
int main(int argc, char** argv)
{
String cascadeFilePath = "D:/opencv/build/etc/haarcascades/haarcascade_frontalface_alt.xml";
CascadeClassifier face_cascade;
if (!face_cascade.load(cascadeFilePath))
{
cout << "could not load haar dara.." << endl;
return -1;
}
Mat src, src_gray;
src = imread("E:/result/666.jpg", IMREAD_COLOR);
cvtColor(src, src_gray, COLOR_BGR2GRAY);
//imshow("原图", src);
equalizeHist(src_gray, src_gray);
vector<Rect> faces;
face_cascade.detectMultiScale(src_gray, faces, 1.1, 2, 0);
for (size_t t = 0; t < faces.size(); t++)
{
rectangle(src, faces[t], Scalar(0, 0, 255), 2, 8, 0);
}
imshow("faces", src);
waitKey(0);
return 0;
}
来源:CSDN
作者:张林克
链接:https://blog.csdn.net/weixin_45292794/article/details/104575749