概述:OpenCV介绍与环境

OpenCV介绍

  • opencv计算机视觉开源库,算法涉及图像处理与机器学习。
  • Intel公司贡献,俄罗斯工程师贡献大部分C/C++代码。
  • BSD许可,可免费商用。
  • SDK支持Java,Python,IOS,Android。

OpenCV框架

iShot2020-09-2402.26.26.png

示例代码

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main() {
    Mat srcImage = imread("/Users/kanikig/Documents/CProjects/test/test.jpg");
    if (!srcImage.data) {
        std::cout << "Image not loaded";
        return -1;
    }
    imshow("[img]", srcImage);
    waitKey(0);
    return 0;
}

加载、修改、保存图像

色彩空间

  • 位图
  • 灰度
  • RGB真彩色
  • CMYK
  • HSV
  • YUV

加载图像

Mat srcImage = imread(filename, int flags)

flags = 
IMREAD_UNCHANGED (<0) 表示加载原图,不做任何改变
IMREAD_GRAYSCALE (0)表示把原图作为灰度图像加载进来
IMREAD_COLOR (>0) 表示把原图作为RGB图像加载进来(default)

显示图像

namedWindow("窗口1", CV_WINDOW_NORMAL);
	/*
	参数1:窗口的名字
	参数2:窗口类型,CV_WINDOW_AUTOSIZE 时表明窗口大小等于图片大小。不可以被拖动改变大小。
	CV_WINDOW_NORMAL 时,表明窗口可以被随意拖动改变大小。
	*/

imshow("窗口1", srcImage);	//在“窗口1”这个窗口输出图片。

修改图像

Mat gray_image;
cvtColor(image, gray_image, COLOR_RGB2GRAY);

保存图像

imwrite(filename, Input_img, param);
	/*
	保存图像文件到指定目录路径
	只有8位、16位的PNG、JPG、Tiff文件格式而且是单通道或者三通道的BGB的图像才可以通过这种方式保存
	保存PNG格式的时候可以保存透明通道的图片
	可以指定压缩参数
	*/

矩阵的掩膜操作

获取像素指针

//确定图像深度,若为false报错
CV_Assert(myImage.depth() == CV_8U); 
//获取当前行指针(row从0开始)
const uchar*  current= Image.ptr<uchar>(row);
//获得当前像素点像素值
p(row,col) =current[col];
//像素范围处理,保证RGB在0-255
saturate_cast<ucahr>();

掩膜(mask)操作

//定义掩膜
Mat kernel = (Mat_<char>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
//掩膜操作
filter2D(src, dst, src.depth(), kernel);

//程序计时
t = (double)getTickCount();
t = (double)getTickCount() - t)/getTickFrequency();

Mat对象

Mat对象使用

//构造函数
Mat dst;
dst = Mat(int rows, int cols, int type);
dst = Mat(Size size, int type);
//等价于
dst.create(Size size, int type);
//其中前两个参数分别表示行(row)跟列(column)、第三个CV_8UC3中的8表示每个通道占8位、U表示无符号、C表示Char类型、3表示通道数目是3,第四个参数是向量表示初始化每个像素值是多少,向量长度对应通道数目一致
dst = Mat M(2,2,CV_8UC3, Scalar(0,0,255))

dst = Scalar(127, 0, 255); //将图像设置成单一灰度和颜色
//常用方法
Image.cols;
Image.rows;
Image.copyTo(mat);
Image.clone();
Image.convertTo(Mat dst, int type);
Image.channels(); //RGB=3, 灰度图=1
Image.depth(); //一般写-1
Image.empty();
uchar* ptr(row);

Mat dst = src.clone();
src.copyTo(dst);

Mat定义数组

Mat C = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
eye(int rows, int cols, int type) //生产单位矩阵

图像操作

读写图像

imread();
imwrite();

读写像素

int px = Image.at<uchar>(row, col);

修改像素

int height = image.rows;
int width = image.cols;
int channels = image.channels();
printf("height=%d width=%d channels=%d", height, width, channels);
for (int row = 0; row < height; row++) {
  for (int col = 0; col < width; col++) {
    if (channels == 3) {
      image.at<Vec3b>(row, col)[0] = 0; // blue
      image.at<Vec3b>(row, col)[1] = 0; // green
    }
  }
}
//Vec3b对应三通道的顺序是blue、green、red的uchar类型数据。Vec3f对应三通道的float类型数据
//把CV_8UC1转换到CV32F1实现如下:src.convertTo(dst, CV_32F);

//反色
bitwise_not(src, dst);

图像混合

线性混合理论

iShot2020-09-2402.22.30.png

相关API

AddWeighted( const CvArr* src1, double alpha,const CvArr* src2, double beta,double gamma, CvArr* dst );
/*
参数1:src1,第一个原数组
参数2:alpha,第一个数组元素权重
参数3:src2第二个原数组
参数4:beta,第二个数组元素权重
参数5:gamma,图1与图2作和后添加的数值(默认0)。不要太大,不然图片一片白。总和等于255以上就是纯白色了。
参数6:dst,输出图片
*/

调整亮度与对比度

理论

iShot2020-09-2402.22.16.png

示例

Mat new_image = Mat::zeros( image.size(), image.type() ); //创建一张跟原图像大小和类型一致的空白图像、像素值初始化为0
saturate_cast<uchar>(value) //确保值大小范围为0~255之间
Mat.at<Vec3b>(y,x)[index]=value //给每个像素点每个通道赋值


int height = image.rows;
int width = image.cols;
double alpha = 1.2;
double beta = 50;
output = Mat::zeros(image.size(), image.type());
for (int row = 0; row < height; row++) {
  for (int col = 0; col < width; col++) {
    output.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(alpha * image.at<Vec3b>(row, col)[0] + beta); //blue
    output.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(alpha * image.at<Vec3b>(row, col)[1] + beta); //green
    output.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(alpha * image.at<Vec3b>(row, col)[2] + beta); //red
    }
  }
}

绘制形状和文字

Point与Scalar

Point p;
p.x = 10;
p.y = 8;
//等价于
p = Point(10,8);

Scalar(B, G, R);

绘制形状

(LINE_4, LINE_8, LINE_AA) //AA反锯齿
line(frame, beginPoint, endPoint, Scalar(0, 0, 255), 2); //起点为beginPoint,终点是endPoint,颜色是红色,线宽是2,shift为默认值

Rect rect = Rect(200, 100, 300, 300); // 起始xy,宽高
rectangle(Image, rect, color, 2, LINE_8);

eclipse(Image, Point(Image.cols/2, Image.rows/2), Size, 90, 0, 360, color, 2);

circle(central point, radius, color, 2);

//多边形
Point pts[1][5];
pts[0][0] = Point(100,100);
fillpoly(Image, in, out, 1, color, 8);

文字

putText(
		cv::Mat& img, // 待绘制的图像
		const string& text, // 待绘制的文字
		cv::Point origin, // 文本框的左下角
		int fontFace, // 字体 (如cv::FONT_HERSHEY_PLAIN)
		double fontScale, // 尺寸因子,值越大文字越大
		cv::Scalar color, // 线条的颜色(RGB)
		int thickness = 1, // 线条宽度
		int lineType = 8, // 线型(4邻域或8邻域,默认8邻域)
		bool bottomLeftOrigin = false // true='origin at lower left'
	);

随机绘制

RNG rng(12345);
Point pt1;
Point pt2;
for (int i =0; i < 1000; i++){
  pt1.x = rng.uniform(0, Image.cols);
  pt1.y = rng.uniform(0, Image.rows);
  pt2.x = rng.uniform(0, Image.cols);
  pt2.y = rng.uniform(0, Image.rows);
  Scalar color = Scalar(rng.uniform(0,255), rng.uniform(0,255), rng.uniform(0,255));
  if (wairKey(50) > 0){
    break; //50秒循环一次,直到按键
  }
  line(Image, pt1, pt2, color, 1, 8);
}

模糊图像一

原理

iShot2020-09-2403.32.36.png

iShot2020-09-2403.32.43.png

iShot2020-09-2403.32.48.png

示例

blur(Mat src, Mat dst, Size(xradius, yradius), Point(-1,-1));
GaussianBlur(Mat src, Mat dst, Size(11, 11), sigmax, sigmay); //Size(x, y), x, y 必须是正数而且是奇数

图像模糊二

中值滤波

iShot2020-09-2404.20.14.png

双边滤波

iShot2020-09-2404.23.30.png

示例

medianBlur(Mat src, Mat dest, ksize);
bilateralFilter(src, dest, 15, 100, 3);

/* 15 –计算的半径d,半径之内的像数都会被纳入计算,如果提供-1 则根据sigma space参数取值
	 100 – sigma color 决定多少差值之内的像素会被计算
 	 3 – sigma space 如果d的值大于0则声明无效,否则根据它来计算d值
中值模糊的ksize大小必须是大于1而且必须是奇数。
*/

膨胀与腐蚀

iShot2020-09-2803.40.02.png

腐蚀

iShot2020-09-2803.40.14.png

膨胀

iShot2020-09-2803.40.09.png

示例

kernel = getStructuringElement(int shape, Size ksize, Point anchor);
/*
 - 形状 (MORPH_RECT \MORPH_CROSS \MORPH_ELLIPSE)
 - 大小 奇数 Size(1,1);
 - 锚点 默认是Point(-1, -1)意思就是中心像素
*/
dilate(src, dst, kernel);
erode(src, dst, kernel);

//动态调整结构元素大小
createTrackbar(const String & trackbarname, const String winName,  int* value, int count, Trackbarcallback func, void* userdata=0);
/*
形式参数一、trackbarname:滑动空间的名称;
形式参数二、winname:滑动空间用于依附的图像窗口的名称;
形式参数三、value:初始化阈值;
形式参数四、count:滑动控件的刻度范围;
形式参数五、TrackbarCallback是回调函数,其定义如下:
*/
void (CV_CDECL *TrackbarCallback)(int pos, void* userdata);

demo

#include <opencv2/opencv.hpp> 
#include <iostream> 
using namespace cv;

Mat src, dst;
char OUTPUT_WIN[] = "output image";
int element_size = 3;
int max_size = 21;
void CallBack_Demo(int, void*);
int main(int argc, char** argv) {
	
	src = imread("D:/vcprojects/images/test1.png");
	if (!src.data) {
		printf("could not load image...\n");
		return -1;
	}
	namedWindow("input image", CV_WINDOW_AUTOSIZE);
	imshow("input image", src);

	namedWindow(OUTPUT_WIN, CV_WINDOW_AUTOSIZE);
  //上一与下二参数必须一致
	createTrackbar("Element Size :", OUTPUT_WIN, &element_size, max_size, CallBack_Demo);
	CallBack_Demo(0, 0);

	waitKey(0);
	return 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, dst, structureElement, Point(-1, -1), 1);
	erode(src, dst, structureElement);
	imshow(OUTPUT_WIN, dst);
	return;
}

形态学操作

开 open

iShot2020-09-2804.12.31.png

闭 close

iShot2020-09-2804.12.46.png

形态学梯度 Morphological Gradient

iShot2020-09-2804.23.08.png

顶帽 top hat

iShot2020-09-2804.23.13.png

黑帽 black hat

iShot2020-09-2804.23.19.png

示例

Mat kernel = getStructuringElement(MORPH_RECT, Size(11,11), Point(-1, -1));
morphologyEx(src, dst, CV_MOP_BLACKHAT, kernel);
/*
- Mat src – 输入图像
 - Mat dest – 输出结果
 - int OPT – CV_MOP_OPEN/ CV_MOP_CLOSE/ CV_MOP_GRADIENT / CV_MOP_TOPHAT/ CV_MOP_BLACKHAT 形态学操作类型
Mat kernel 结构元素
int Iteration 迭代次数,默认是1
*/

形态学操作 提取水平垂直线

原理

iShot2020-09-2902.29.04.png

iShot2020-09-2902.29.10.png

iShot2020-09-2902.29.15.png

iShot2020-09-2902.29.22.png

实现思路

iShot2020-09-2902.29.32.png

示例

//二值化
adaptiveThreshold(
Mat src, // 输入的灰度图像
Mat dest, // 二值图像
double maxValue, // 二值图像最大值
int adaptiveMethod // 自适应方法,只能其中之一 – 
		         // ADAPTIVE_THRESH_MEAN_C , ADAPTIVE_THRESH_GAUSSIAN_C 
int thresholdType,// 阈值类型
int blockSize, // 块大小
double C // 常量C 可以是正数,0,负数
);
adaptiveThreshold(~gray_src, binImg, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);
// ~ 反色 bitwise_not(src, dst);

	// 水平结构元素
Mat hline = getStructuringElement(MORPH_RECT, Size(src.cols / 16, 1), Point(-1, -1));
	// 垂直结构元素
Mat vline = getStructuringElement(MORPH_RECT, Size(1, src.rows / 16), Point(-1, -1));
	// 矩形结构,可以用于OCR
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));

morphologyEx(src, dst, CV_MOP_BLACKHAT, hline);

图像上采样和下采样

图像金字塔

iShot2020-09-2903.07.24.png

iShot2020-09-2903.08.01.png

iShot2020-09-2903.07.36.png

iShot2020-09-2903.07.49.png

示例

pyrUp(Mat src, Mat dst, Size(src.cols*2, src.rows*2)) 
//生成的图像是原图在宽与高各放大两倍
pyrDown(Mat src, Mat dst, Size(src.cols/2, src.rows/2))
//生成的图像是原图在宽与高各缩小1/2
// DOG
	Mat gray_src, g1, g2, dogImg;
	cvtColor(src, gray_src, CV_BGR2GRAY);
	GaussianBlur(gray_src, g1, Size(5, 5), 0, 0);
	GaussianBlur(g1, g2, Size(5, 5), 0, 0);
	subtract(g1, g2, dogImg, Mat());

	// 归一化显示
	normalize(dogImg, dogImg, 255, 0, NORM_MINMAX);
	imshow("DOG Image", dogImg);

基本阈值操作

阈值概念

iShot2020-09-2903.25.07.png

阈值类型

iShot2020-09-2903.25.56.png

iShot2020-09-2903.26.08.png

iShot2020-09-2903.26.14.png

iShot2020-09-2903.26.46.png

iShot2020-09-2903.26.52.png

iShot2020-09-2903.27.06.png

示例

cvtColor(src, gray_src, CV_BGR2GRAY);
threshold(src, dst, 0, 255, THRESH_TRIANGLE | type_value);
//OISU和TRIANGLE自动计算阈值,故dst后阈值写0被忽略

enum ThresholdTypes {
    THRESH_BINARY     = 0,
    THRESH_BINARY_INV = 1,
    THRESH_TRUNC      = 2,
    THRESH_TOZERO     = 3,
    THRESH_TOZERO_INV = 4,
    THRESH_MASK       = 7,
    THRESH_OTSU       = 8,
    THRESH_TRIANGLE   = 16
};

自定义线型滤波

卷积概念

iShot2020-09-2904.00.29.png

iShot2020-09-2904.00.43.png

iShot2020-09-2904.02.52.png

iShot2020-09-2904.02.15.png

常见算子

iShot2020-09-2904.03.02.png

自定义卷积模糊

filter2D(
Mat src, //输入图像
Mat dst, // 模糊图像
int depth, // 图像深度32/8
Mat kernel, // 卷积核/模板
Point anchor, // 锚点位置
double delta // 计算出来的像素+delta
)

示例

	// Sobel X 方向
Mat kernel_x = (Mat_<int>(3, 3) << -1, 0, 1, -2,0,2,-1,0,1);

//Sobel Y 方向
Mat kernel_y = (Mat_<int>(3, 3) << -1, -2, -1, 0,0,0, 1,2,1);

// 拉普拉斯算子
Mat kernel = (Mat_<int>(3, 3) << 0, -1, 0, -1, 4, -1, 0, -1, 0);

filter2D(src, dst, -1, kernel_x, Point(-1, -1), 0.0);

边缘处理

卷积边缘问题

iShot2020-09-2904.13.20.png

处理边缘

iShot2020-09-2904.13.34.png

示例

copyMakeBorder
 Mat src, // 输入图像
 Mat dst, // 添加边缘图像
 int top, // 边缘长度,一般上下左右都取相同值,
 int bottom,
 int left,
 int right, 
 int borderType // 边缘类型
 Scalar value 

while(true){
  c = waitKey(500);
  //esc
  if((char)c == 27)
  {break;}
}

Sobel算子

卷积应用-边缘提取

iShot2020-10-0204.39.29.png

iShot2020-10-0204.39.51.png

iShot2020-10-0204.40.04.png

iShot2020-10-0204.40.15.png

iShot2020-10-0204.49.30.png

示例

Sobel (
InputArray Src // 输入图像
OutputArray dst// 输出图像,大小与输入图像一致
int depth // 输出图像深度. 
Int dx.  // X方向,几阶导数
int dy // Y方向,几阶导数. 
int ksize, SOBEL算子kernel大小,必须是1357
double scale  = 1
double delta = 0
int borderType = BORDER_DEFAULT
)

//Sobel 的改进
Scharr (
InputArray Src // 输入图像
OutputArray dst// 输出图像,大小与输入图像一致
int depth // 输出图像深度. 
Int dx.  // X方向,几阶导数
int dy // Y方向,几阶导数. 
double scale  = 1
double delta = 0
int borderType = BORDER_DEFAULT
)
//步骤
GaussianBlur( src, dst, Size(3,3), 0, 0, BORDER_DEFAULT );
cvtColor( src,  gray, COLOR_RGB2GRAY );
Scharr(gray_src, xgrad, CV_16S, 1, 0);
Scharr(gray_src, ygrad, CV_16S, 0, 1);
// Sobel(gray_src, xgrad, CV_16S, 1, 0, 3);
// Sobel(gray_src, ygrad, CV_16S, 0, 1, 3);
convertScaleAbs(xgrad, xgrad);// 计算图像A的像素绝对值,输出到图像B
convertScaleAbs(ygrad, ygrad);
addWeighted( xgrad, 0.5,ygrad, 0.5, 0, xygrad);

Laplance算子

理论

iShot2020-10-0308.21.04.png

iShot2020-10-0308.21.20.png

示例

Laplacian(
InputArray src,
OutputArray dst,
int depth, //深度CV_16S
int kisze, // 3
double scale = 1,
double delta =0.0,
int borderType = 4
)
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边缘检测

算法介绍

iShot2020-10-0308.33.22.png

iShot2020-10-0308.33.30.png

iShot2020-10-0308.33.36.png

iShot2020-10-0308.40.36.png

示例

Canny
InputArray src, // 8-bit的输入图像
OutputArray edges,// 输出边缘图像, 一般都是二值图像,背景是黑色
double threshold1,// 低阈值,常取高阈值的1/2或者1/3
double threshold2,// 高阈值
int aptertureSize,// Soble算子的size,通常3x3,取值3
bool L2gradient // 选择 true表示是L2来归一化,否则用L1归一化

cvtColor(src, gray_src, CV_BGR2GRAY);
blur(gray_src, gray_src, Size(3, 3), Point(-1, -1), BORDER_DEFAULT);
Canny(gray_src, edge_output, t1_value, t1_value * 2, 3, false);

霍夫变换-直线检测

理论

iShot2020-10-0308.48.47.png

iShot2020-10-0308.49.31.png

iShot2020-10-0308.49.25.png

iShot2020-10-0308.50.04.md.png

补充资料:https://blog.csdn.net/leonardohaig/article/details/87907462?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight

示例

HoughLines(
InputArray src, // 输入图像,必须8-bit的灰度图像
OutputArray lines, // 输出的极坐标来表示直线
double rho, // 生成极坐标时候的像素扫描步长
double theta, //生成极坐标时候的角度步长,一般取值CV_PI/180
int threshold, // 阈值,只有获得足够交点的极坐标点才被看成是直线
double srn=0;// 是否应用多尺度的霍夫变换,如果不是设置0表示经典霍夫变换
double stn=0;//是否应用多尺度的霍夫变换,如果不是设置0表示经典霍夫变换
double min_theta=0; // 表示角度扫描范围 0 ~180之间, 默认即可
double max_theta=CV_PI
) // 一般情况是有经验的开发者使用,需要自己反变换到平面空间

HoughLinesP(
InputArray src, // 输入图像,必须8-bit的灰度图像
OutputArray lines, // 输出的极坐标来表示直线
double rho, // 生成极坐标时候的像素扫描步长,一般为1
double theta, //生成极坐标时候的角度步长,一般取值CV_PI/180
int threshold, // 阈值,只有获得足够交点的极坐标点才被看成是直线
double minLineLength=0;// 最小直线长度
double maxLineGap=0;// 最大间隔
)
// extract edge
Canny(src, src_gray, 150, 200);
cvtColor(src_gray, dst, CV_GRAY2BGR);
imshow("edge image", src_gray);

vector<Vec4f> plines;
HoughLinesP(src_gray, plines, 1, CV_PI / 180.0, 10, 0, 10);
Scalar color = Scalar(0, 0, 255);
for (size_t i = 0; i < plines.size(); i++) {
		Vec4f hline = plines[i];
		line(dst, Point(hline[0], hline[1]), Point(hline[2], hline[3]), color, 3, LINE_AA);
}