opencv学习笔记(更新中…)
写在前面,opencv官方文档。
地址:https://docs.opencv.org/4.x/index.html
ps:markdown导入csdn是真麻烦,本地图片需要单独上再传调整html格式。
一、基础操作
1、图像的读取与显示
函数原型:
读取图像:imread()
显示图像:imshow()
代码演示:
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
Mat src = imread("D:/ziliao/codelearning/opencv/code/aa1.png",IMREAD_GRAYSCALE); //BGR
//IMREAD_GRAYSCALE加载灰度图
if (src.empty()) {
cout << "could not load image..." << endl;
return -1;
}
//手动创建窗口
namedWindow("输入窗口", WINDOW_FREERATIO);
imshow("输入窗口", src); //只支持8位图像或浮点图像
waitKey(0); //程序阻塞 图片窗口停留
destroyAllWindows();
return 0;
}
2、色彩空间转换与读出
2.1、色彩空间转换函数-- cvtColor()
COLOR_BGR2GRAY = 6 彩色到灰度
COLOR_GRAY2BGR = 8 灰度到彩色
COLOR_BGR2HSV = 40 BGR到HSV
COLOR_HSV2BGR = 54 HSV到BGR
2.2 图像保存- imwrite(pra1, pra2)
pra1–图像保存的路径
pra2–图像内存对象
代码示例
void QuickDemo::colorSpace_Demo(Mat& image) {
Mat gray, hsv;
cvtColor(image, hsv, COLOR_BGR2HSV);
//H 0~180 S、V 0-255
//H、S表示颜色 V表示亮度
cvtColor(image, gray, COLOR_BGR2GRAY);
imshow("HSV", hsv);
imshow("灰度", gray);
imwrite("D:/ziliao/codelearning/opencv/code/hsv.png", hsv);
imwrite("D:/ziliao/codelearning/opencv/code/gray.png", gray);
}
3、图像的创建与赋值
C++中的Mat对象与创建
python中numpy数组对象
- 赋值
- 拷贝/克隆
Mat的基本结构
代码示例
void QuickDemo::mat_creation_demo(Mat& image) {
Mat m1, m2;
m1 = image.clone(); //图像克隆 拷贝
image.copyTo(m2);
//创建空白图像
Mat m3 = Mat::zeros(Size(400,400), CV_8UC3); //8位无符号数 3通道
//opencv运算符都已被重载 对mat的操作可以直接使用
//赋值
m3 = Scalar(255, 0, 0);
//宽度 高度 通道
cout << "width:" << m3.cols << " height:" << m3.rows << " channels:" << m3.channels() << endl;
//cout << m3 << endl;
//imshow("创建图像", m3);
Mat m4 = m3;
Mat m5 = m3.clone(); //copyTo() 也是一样浅拷贝
//m4 = Scalar(0, 255, 255); //m3被改变 深拷贝
m5 = Scalar(0, 255, 255); //m3不被改变 浅拷贝
imshow("m3", m3);
}
4、图像像素的读写操作
操作像素,即操作mat数组中的值;
- 单通道
- 双通道
void QuickDemo::pixel_visit_demo(Mat& image) {
int w = image.cols;
int h = image.rows;
int dims = image.channels();
//使用数组下标
for (int row = 0; row < h; row++) {
for (int col = 0; col < w; col++) {
//反相
if (dims == 1) {
int pv = image.at<uchar>(row, col);
image.at<uchar>(row, col) = 255 - pv;
}
if (dims == 3) {
Vec3b bgr = image.at<Vec3b>(row, col);
image.at<Vec3b>(row, col)[0] = 255 - bgr[0];
image.at<Vec3b>(row, col)[1] = 255 - bgr[1];
image.at<Vec3b>(row, col)[2] = 255 - bgr[2];
}
}
}
//使用指针
for (int row = 0; row < h; row++) {
uchar* current_row = image.ptr<uchar>(row);
for (int col = 0; col < w; col++) {
//反相
if (dims == 1) {
int pv = *current_row;
*current_row++ = 255 - pv;
}
if (dims == 3) {
*current_row++ = 255 - *current_row;
*current_row++ = 255 - *current_row;
*current_row++ = 255 - *current_row;
}
}
}
imshow("像素读写演示", image);
}
5、图像像素的算术操作
saturate_cast<uchar>() : 防止溢出,限定在0~255范围
图像的加减乘除运算:
void QuickDemo::operators_demo(Mat& image) {
Mat dst;
Mat m = Mat::zeros(image.size(), image.type());
m = Scalar(2, 2, 2);
dst = image / m;
dst = image * 2;
//运算操作
multiply(image, m, dst);
add(image, m, dst);
divide(image, m, dst);
subtract(image, m, dst);
imshow("操作", dst);
}
6、键盘响应
key = waitKey(delay) 函数:
key – 键值的ascci码
delay – 延时时间(ms)
//键盘响应示例
void QuickDemo::key_demo(Mat& image) {
Mat dst = Mat::zeros(image.size(), image.type());
while (true) {
char c = waitKey(1); //c为按下的键的ascii码
if (c == 27) { //esc
break;
}
if (c == 49) { //#1
cout << "you enter key #1" << endl;
cvtColor(image, dst, COLOR_BGR2GRAY);
}
if (c == 50) { //#2
cout << "you enter key #2" << endl;
cvtColor(image, dst, COLOR_BGR2HSV);
}
if (c == 51) { //#3
cout << "you enter key #3" << endl;
dst = Scalar(50, 50, 50);
add(image, dst, dst );
}
imshow("键盘响应", dst);
}
}
7、图像像素的逻辑操作
void QuickDemo::bitwise_demo(Mat& image) {
Mat m1 = Mat::zeros(Size(256, 256), CV_8UC3);
Mat m2 = Mat::zeros(Size(256, 256), CV_8UC3);
//绘制矩形色块
rectangle(m1, Rect(100, 100, 80, 80), Scalar(255, 255, 0), -1, LINE_8, 0);
rectangle(m2, Rect(150, 150, 80, 80), Scalar(0, 255, 255), -1, LINE_8, 0);
imshow("m1", m1);
imshow("m2", m2);
Mat dst1;
Mat dst2;
Mat dst3;
bitwise_and(m1, m2, dst1); //位逻辑与
bitwise_or(m1, m2, dst2); //或
bitwise_not(m1, dst3); //非
imshow("and", dst1);
imshow("or", dst2);
imshow("not", dst3);
}
输出结果:
8、通道分离与合并
//函数原型:
通道分离:split()
通道合并:merge()
通道混合:void cv::mixChannels(
const Mat * src, //输入图像
size_t nsrcs, //输入图像个数
Mat * dst, //输出图像
size_t ndsts, //输出个数
const int * fromTo, //混合对
size_t npairs //对数
)
代码示例
//示例
void QuickDemo::channels_demo(Mat& image) {
vector<Mat> mv; //容器装载多个图像
split(image, mv); //通道分离
imshow("B", mv[0]);
imshow("G", mv[1]);
imshow("R", mv[2]);
Mat dst;
merge(mv, dst); //合并通道
imshow("合并", dst);
//通道混合
int from_to[] = { 0,2,1,1,2,0 };//通道 0->2 1->1 2->0
mixChannels(&image, 1, &dst, 1, from_to, 3);//参数:(输入,输入个数,输出,输出个数,混合参数对,对数)
imshow("通道混合", dst);
}
9、图像像素值统计
minMaxLoc
meanStdDev方法
void QuickDemo::pixel_statistic_demo(Mat& image) {
double minv, maxv;
Point minLoc, maxLoc;
vector<Mat> mv;
split(image, mv);
for (int i = 0; i < mv.size(); i++) {
//提取最大最小像素值 位置
minMaxLoc(mv[i], &minv, &maxv, &minLoc, &maxLoc, Mat());
cout << "no.channels:" << i << "min value" << minv << "max value" << maxv << endl;
}
Mat mean, stddev;
//灰度均值 方差统计
meanStdDev(image, mean, stddev);
cout << "means:" << mean << endl;
cout << "stddev" << stddev << endl;
//opencv库中运算符均已被重载,可直接输出任何格式的数据类型
}
10、几何形状绘制
//常见形状绘制
void QuickDemo::drawing_demo(Mat& image) {
Rect rect;
rect.x = 100;
rect.y = 100;
rect.width = 200;
rect.height = 200;
//绘制图形
rectangle(image, rect, Scalar(0, 0, 255), -1, 8, 0);
circle(image, Point(350, 400), 15, Scalar(0, 0, 255), -1, 8, 0);
//line 线
//ellipse 椭圆
imshow("图形绘制", image);
}
//任意多边形绘制
void QuickDemo::polyline_drawing_demo() {
Mat canvas = Mat::zeros(Size(512, 512), CV_8UC3);
Point p1(100, 100);
Point p2(350, 100);
Point p3(450, 280);
Point p4(320, 450);
Point p5(80, 400);
vector<Point> pts;
pts.push_back(p1);
pts.push_back(p2);
pts.push_back(p3);
pts.push_back(p4);
pts.push_back(p5);
//方法一
polylines(canvas, pts, true, Scalar(0, 255, 0), 2,LINE_AA, 0);
//方法二:轮廓绘制
vector<vector<Point>> contours;
contours.push_back(pts);
drawContours(canvas, contours, -1, Scalar(255, 0, 0), -1);
imshow("多边形", canvas);
}
11、鼠标操作与响应(鼠标绘制roi区域)
实现用鼠标绘制图像中的roi区域
//鼠标操作与响应
Point sp(-1, -1);
Point ep(-1, -1);
Mat temp;
static void on_draw(int event, int x, int y, int flags, void* userdata) {
Mat image = *((Mat*)userdata);
//起点
if (event == EVENT_LBUTTONDOWN) {
sp.x = x;
sp.y = y;
cout << "start point: " << sp << endl;
}
//终点
else if(event==EVENT_LBUTTONUP){
ep.x = x;
ep.y = y;
int dx = abs(ep.x - sp.x);
int dy = abs(ep.y - sp.y);
Rect box(sp.x, sp.y, dx, dy);
rectangle(image, box, Scalar(0, 0, 255), 2, 8, 0);
imshow("mouse drawing", image);
//提取roi区域
imshow("ROI region", image(box));
//到达终点 更新起点坐标
sp.x = -1;
sp.y = -1;
}
//移动过程实时中绘制
else if (event == EVENT_MOUSEMOVE) {
if (sp.x > 0 && sp.y > 0) {
ep.x = x;
ep.y = y;
int dx = abs(ep.x - sp.x);
int dy = abs(ep.y - sp.y);
Rect box(sp.x, sp.y, dx, dy);
//更新原图 防止绘制叠加
temp.copyTo(image);
rectangle(image, box, Scalar(0, 0, 255), 2, 8, 0);
imshow("mouse drawing", image);
}
}
}
void QuickDemo::mouse_drawing_demo(Mat &image) {
namedWindow("mouse drawing", WINDOW_AUTOSIZE);
setMouseCallback("mouse drawing", on_draw,(void*)(&image));
imshow("mouse drawing", image);
//用于更新原图
temp = image.clone();
}
实现效果
12、图像像素类型转换与归一化
//像素类型转换与归一化
void QuickDemo::norm_demo(Mat& image) {
Mat dst;
cout << image.type() << endl; //CV_8UC3
//类型转换
image.convertTo(image, CV_32F);
cout << image.type() << endl;
//数据类型转换后,必须进行归一化处理
//数据归一化
normalize(image, dst, 1.0, 0, NORM_MINMAX);//转换到0-1
cout << dst.type() << endl;
imshow("归一化前", image);
imshow("归一化后", dst);
}
处理结果
13、图像的放缩与插值
void QuickDemo::resize_demo(Mat& image) {
Mat zoomin, zoomout;
int h = image.rows;
int w = image.cols;
resize(image, zoomout, Size(w / 2, h / 2), 0, 0, INTER_LINEAR);
resize(image, zoomin, Size(w * 1.5, h * 1.5), 0, 0, INTER_LINEAR);
imshow("zoomout", zoomout); //缩小
imshow("zoomin", zoomin); //放大
}
14、图像翻转/旋转
void QuickDemo::flip_demo(Mat& image) {
Mat dst;
flip(image, dst, 0); //上下翻转
flip(image, dst, 1); //左右翻转
flip(image, dst, -1); //旋转180°
imshow("图像翻转", dst);
}
void QuickDemo::rotate_demo(Mat& image) {
Mat dst, M;
int w = image.cols;
int h = image.rows;
//2x3的矩阵 旋转矩阵 旋转中心坐标
M = getRotationMatrix2D(Point2f(w / 2, h / 2), 45, 1.0); //旋转中心,角度
//不改变旋转图片大小 计算图像尺寸
double cos = abs(M.at<double>(0, 0));
double sin = abs(M.at<double>(0, 1));
int nw = cos * w + sin * h;
int nh = sin * w + cos * h;
//新的旋转中心
M.at<double>(0, 2) += (nw / 2 - w / 2);
M.at<double>(1, 2) += (nh / 2 - h / 2);
cout << "旋转矩阵"<< M << endl;
warpAffine(image, dst, M, Size(nw,nh), INTER_LINEAR, 0, Scalar(255, 255, 0));
imshow("旋转", dst);
}
旋转效果

15、相机驱动/视频文件读取
//相机驱动
void QuickDemo::video_demo(Mat& image) {
VideoCapture capture(0);
// VideoCapture capture("路径"); //读取视频
Mat frame;
while (true) {
capture.read(frame);
if (frame.empty()) {
break;
}
imshow("frame",frame);
// to do something...
int c = waitKey(1);
if (c == 27) {
break;
}
}
capture.release();
}
16、图像直方图
- 灰度直方图
- 颜色直方图
图像直方图是图像像素的统计学特征、计算代价较小,具有图像平移、旋转、缩放不变性等众多优点。广泛应用于图像处理的各个领域,特别是灰度图像的阈值分割、基于颜色的图像检索以及图像分类、反向投影跟踪。
//函数原型
calcHist(&bgr_plane[0],1,0,Mat(),b_hist,1,bins,ranges);
cv.calcHist([image],[i],None,[256],[0,256]);
//注:Bins是指直方图的大小范围,对于像素值取值在0~255之间的,最少有256个bin,此外还可以有16、32、48、128等。
void QuickDemo::showHistogram_demo(Mat& image) {
Mat image_gray, hist; //灰度图,直方图
cvtColor(image, image_gray, COLOR_BGR2GRAY); //灰度化
imshow(" image_gray", image_gray); //显示灰度图像
//获取图像直方图
int histsize = 256;
float ranges[] = { 0,256 };
const float* histRanges = { ranges };
calcHist(&image_gray, 1, 0, Mat(), hist, 1, &histsize, &histRanges, true, false);
//创建直方图显示图像
int hist_h = 300;//直方图的图像的高
int hist_w = 512; //直方图的图像的宽
int bin_w = hist_w / histsize;//直方图的等级
Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0));//绘制直方图显示的图像
//绘制并显示直方图
normalize(hist, hist, 0, hist_h, NORM_MINMAX, -1, Mat());//归一化直方图
for (int i = 1; i < histsize; i++)
{
line(histImage, Point((i - 1) * bin_w, hist_h - cvRound(hist.at<float>(i - 1))),
Point((i)*bin_w, hist_h - cvRound(hist.at<float>(i))), Scalar(255, 0, 0), 2, 8, 0);
}
imshow("histImage", histImage);
//直方图均衡化
Mat image_enhanced;
equalizeHist(image_gray, image_enhanced);//直方图均衡化
imshow(" image_enhanced", image_enhanced); //显示增强图像
//二维直方图
//直方图匹配:将一张图像的直方图转变为规定形状的直方图而进行图像增强的方法
}
效果演示:
17、图像卷积操作
opencv自带了各种卷积操作的api函数
示例
void QuickDemo::blur_demo(Mat& image) {
//自带卷积操作函数
Mat dst;
blur(image, dst, Size(2, 2), Point(-1, -1)); //平滑
imshow("图像模糊", dst);
bilateralFilter(image, dst, 0, 100, 10); //高斯双边滤波
imshow("高斯双边模糊", dst);
}
高斯双边滤波美颜效果
手动卷积操作
//手动卷积
void QuickDemo::convlution_demo(Mat& image) {
//sobel边缘检测算子
Mat Kernel_sobel = (Mat_<double>(3, 3) <<
-1, 0, 1,
-2, 0, 2,
-1, 0, 1);
//灰度化
Mat image_gray;
cvtColor(image, image_gray, COLOR_BGR2GRAY);
imshow("gray", image_gray);
//参数
Mat InputImage = image_gray;
Mat OutputImage = Mat::zeros(image_gray.size(), image_gray.type());
Mat kernel = Kernel_sobel;
//计算卷积核的半径
int sub_x = kernel.cols / 2;
int sub_y = kernel.rows / 2;
//遍历图片,除边界以外每个像素
for (int image_y = 0; image_y < InputImage.rows - 2 * sub_y; image_y++)
{
for (int image_x = 0; image_x < InputImage.cols - 2 * sub_x; image_x++)
{
int pix_value = 0;//用来累加每个位置的乘积
for (int kernel_y = 0; kernel_y < kernel.rows; kernel_y++)//对每一个点根据卷积模板进行卷积
{
for (int kernel_x = 0; kernel_x < kernel.cols; kernel_x++)
{
double weight = kernel.at<double>(kernel_y, kernel_x);
int value = (int)InputImage.at<uchar>(image_y + kernel_y, image_x + kernel_x); //此处卷积核局部坐标系原点应该在核中心
pix_value += weight * value;
}
}
//此处卷积核局部坐标系原点应该在核中心,故向右下偏移核的半径距离
OutputImage.at<uchar>(image_y + sub_y, image_x + sub_x) = saturate_cast<uchar>((int)pix_value);//转换为无符号类型 防止溢出
}
}
//此方法忽略了图像半径长度边缘的部分
imshow("sobel", OutputImage);
}