课程目录
002 图像色彩空间转换
- 色彩空间转换函数 cvtColor
COLOR_BGR2GRAY = 6 彩色到灰度
COLOR_GRAY2BGR = 8 灰度到彩色
COLOR_BGR2HSV = 40 BGR到HSV
COLOR_HSV2BGR = 54 HSV到BGR - 图像保存 imwrite
第一个参数是图像保存路径
第二个参数是图像内存对象
003 图像对象的创建和赋值
C++中Mat对象与创建
Python中Numpy数组对象
004 图像像素的读写操作
C++中的像素遍历与访问
- 数组遍历
- 指针方式遍历
005 图像像素的算数操作
saturate_castit; T> (vlaue)
006 滚动条操作演示 - 调整图像亮度
007 滚动条操作演示 - 参数传递度
008 键盘响应操作
009 opencv自带颜色表操作
aplayColorMap(src, dst, COLORMAP)
- src表示输入图像
- dst表示输出图像
- 匹配到的颜色LUT,Opencv支持13中颜色风格的查找表映射
010 图像像素的逻辑操作
011 通道分离与合并
012 图像色彩空间转换
色彩空间与色彩空间转换 - RGB色彩空间
- HSV色彩空间
- YUV色彩空间
- YCrCb色彩空间
API
- 色彩空间转换API cvtColor
- 提取指定色彩范围区域inRange
013 图像像素值统计
像素值统计 - 最小
- 最大
- 均值
- 标准方差
API
- 最大值最小值minMaxLoc
- 计算均值与标准方差meanStdDev
014 图像集合形状绘制
015 随机数与随机颜色
016 多边形填充绘制
017 鼠标操作与响应
018 图像像素类型转换与归一化
Opencv中提供了四中像素归一化方法 - NORM_MINMAX
- NORM_INF
- NORM_L1
- NORM_L2
最常用的就是NORM_MINMAX归一化方法
相关API函数:
normalize { InputArray src, //输入图像 InputOutputArray, dst //输出图像 double alpha = 1, //NORM_MINMAX时候低值 double beta = 0, //NORM_MINMAX时候高值 int norm_type = NORM_L2, //只有alpha int dtype = -1, // 默认类型与src一致 InputArray mask = noArray(), //mask默认值为空 }
019 图像放缩与插值
图像插值
最常见的四种插值算法
- INTER_NEAREST = 0
- INTER_LINEAR = 1
- INTER_CUBIC = 2
- INTER_LANCZOS4 = 4
相关的应用场景:
几何变换、透视变换、插值计算新像素
resize
如果size有值,使用size做缩放插值,否则根据fx与fy卷积
020 图像翻转
021 图像旋转
022 视频文件摄像头调用
023 视频处理与保存
024 图像直方图
图像直方图官方解释:
图像直方图是图像像素值的统计学特征、计算代价较小、具有图像平移、旋转、缩放不变等众多优点,广泛地应用于图像处理的各个领域,特别是灰度图像的阈值分割,基于颜色的图像检索以及图像分类、反向投影跟踪。常见的分类有: - 灰度直方图
- 颜色直方图
Bins指直方图的大小范围,对于像素值取值在0-255之间的,最少有256个bin,此外还可以有16,32,48,128等,256除以bin的大小应该是整数倍。
openCV相关API
calcHist
025 二维直方图
026 直方图均衡化
027 图像卷积操作
028 高斯模糊
029 高斯双边模糊
030 案例:实时人脸检测
源码
quickdemo.cpp
#include <quickopencv.h> void QuickDemo::colorSpace_Demo(Mat &image) { Mat gray, hsv; cvtColor(image, hsv, COLOR_BGR2HSV); cvtColor(image, gray, COLOR_BGR2GRAY); imshow("HSV", hsv); imshow("GRAY", gray); imwrite("./hsv.jpg", hsv); imwrite("./gray.jpg", gray); } void QuickDemo::mat_creation_demo() { // Mat m1, m2; // m1 = image.clone(); // image.copyTo(m2); //创建空白图像 Mat m3 = Mat::zeros(Size(512,512), CV_8UC3); //CV_8UC1 8位unsigned char 1通道 // m3 = 127; m3 = Scalar(127, 127, 127); std::cout << "宽度 " << m3.cols << "高度 " << m3.rows << "通道数" << m3.channels() << std::endl; std::cout << m3 << std::endl; imshow("创建图像", m3); } 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); } void QuickDemo::operators_demo(Mat &image) { // Mat dst; // dst = image + Scalar(50, 50, 50); // dst = image - Scalar(50, 50, 50); // dst = image * Scalar(2, 2, 2); // dst = image / Scalar(2, 2, 2); Mat dst = Mat::zeros(image.size(), image.type()); Mat m = Mat::zeros(image.size(), image.type()); m = Scalar(5, 5, 5); // add(image, m, dst); // substract(image, m, dst); divide(image, m, dst); // multiply(image, m, dst); imshow("加法操作", dst); } static void on_lightness(int b, void *userdata) { Mat image = *((Mat*)userdata); Mat dst = Mat::zeros(image.size(), image.type()); Mat m = Mat::zeros(image.size(), image.type()); addWeighted(image, 1.0, m, 0, b, dst); imshow("亮度与对比度调整", dst); } static void on_contrast(int b, void *userdata) { Mat image = *((Mat*)userdata); Mat dst = Mat::zeros(image.size(), image.type()); Mat m = Mat::zeros(image.size(), image.type()); double contrast = b / 200.0; addWeighted(image, contrast, m, 0.0, 0, dst); imshow("亮度与对比度调整", dst); } void QuickDemo::tracking_bar_demo(Mat &image) { namedWindow("亮度与对比度调整", WINDOW_AUTOSIZE); int lightness = 50; int max_value = 100; int contrast_value = 100; createTrackbar("Value Bar:", "亮度与对比度调整", &lightness, max_value, on_lightness, (void*)(&image)); createTrackbar("Contrast Bar:", "亮度与对比度调整", &contrast_value, 200, on_contrast, (void*)(&image)); on_lightness(50, &image); } void QuickDemo::key_demo(Mat &image) { Mat dst = Mat::zeros(image.size(), image.type());; while (true) { int c = waitKey(100); if (c == 27) // ESC 退出 { break; } if (c == 49) //将图像转换为灰度图像显示 { std::cout << "you input key #1" << std::endl; cvtColor(image, dst, COLOR_BGR2GRAY); } imshow("键盘响应", dst); } } void QuickDemo::color_style_demo(Mat &image) { int colormap[] = { COLORMAP_AUTUMN, COLORMAP_BONE, COLORMAP_JET, COLORMAP_WINTER, COLORMAP_RAINBOW, COLORMAP_OCEAN, COLORMAP_SUMMER, COLORMAP_SPRING, COLORMAP_COOL, COLORMAP_PINK, COLORMAP_HOT, COLORMAP_PARULA, COLORMAP_MAGMA, COLORMAP_INFERNO, COLORMAP_PLASMA, COLORMAP_VIRIDIS, COLORMAP_CIVIDIS, COLORMAP_TWILIGHT, }; Mat dst; int index = 0; while(true) { int c = waitKey(200); if (c == 27) // ESC 退出 { break; } applyColorMap(image, dst, colormap[index % 19]); index++; imshow("颜色风格", dst); } } 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 dst; // bitwise_and(m1, m2, dst); // bitwise_or(m1, m2, dst); // bitwise_not(image, dst); bitwise_xor(m1, m2, dst); imshow("像素位操作", dst); } void QuickDemo::channels_demo(Mat &image) { std::vector<Mat> mv; split(image, mv); imshow("B", mv[0]); imshow("G", mv[1]); imshow("R", mv[2]); Mat dst; mv[1] = 0; mv[2] = 0; merge(mv ,dst); imshow("蓝色", dst); } void QuickDemo::inrang_demo(Mat &image) { Mat hsv; cvtColor(image, hsv, COLOR_BGR2HSV); Mat mask; inRange(hsv, Scalar(35, 43, 46), Scalar(77, 255, 255), mask); Mat redback = Mat::zeros(image.size(), image.type()); redback = Scalar(80, 80, 80); bitwise_not(mask, mask); imshow("mask", mask); image.copyTo(redback, mask); imshow("rio区域提取", redback); } void QuickDemo::pixel_statistic_demo(Mat &image) { double minv, maxv; Point minLoc, maxLoc; std::vector<Mat> mv; split(image, mv); for (int i=0; i<mv.size(); i++) { minMaxLoc(mv[i], &minv, &maxv, &minLoc, &maxLoc, Mat()); std::cout << "No.channel:" << i << " min value " << minv << " max vlaue " << maxv << std::endl; } Mat mean, stddev; meanStdDev(image, mean, stddev); std::cout << "means " << mean << "stddev " << stddev << std::endl; } void QuickDemo::drawing_demo(Mat &image) { Rect rect; rect.x = 200; rect.y = 200; rect.width = 100; rect.height = 100; Mat bg = Mat::zeros(image.size(), image.type()); rectangle(bg, rect, Scalar(0, 0, 255), 2, 8, 0); circle(bg, Point(350, 400), 15, Scalar(255, 255, 255), 2, 8 ,0); RotatedRect rrt; rrt.center = Point(200,200); rrt.size = Size(100,200); rrt.angle = 0.0; ellipse(image, rrt, Scalar(255,255,255), 2, 8); line(bg, Point(100, 100), Point(350,400), Scalar(255,255,255), 2, 8, 0); Mat dst; addWeighted(image, 0.7, bg, 0.3, 0, dst); imshow("绘制演示", dst); } void QuickDemo::random_drawing_demo() { Mat canvas = Mat::zeros(Size(512, 512), CV_8UC3); int w = canvas.cols; int h = canvas.rows; RNG rng(12345); while (true) { int c = waitKey(100); if (c == 27) // 退出 { break; } int x1 = rng.uniform(0, w); int y1 = rng.uniform(0, h); int x2 = rng.uniform(0, w); int y2 = rng.uniform(0, h); // canvas = Scalar(0,0,0); line(canvas, Point(x1,y1), Point(x2,y2), \ Scalar(rng.uniform(0,255), rng.uniform(0,255),rng.uniform(0,255)), \ 1, LINE_AA, 0); imshow("随机绘制演示", canvas); } } 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); std::vector<Point> pts; pts.push_back(p1); pts.push_back(p2); pts.push_back(p3); pts.push_back(p4); pts.push_back(p5); // // 先用polylines绘制, 再用fillPoly填充 // polylines(canvas, pts, true, Scalar(100,255,200), 2, 0); // fillPoly(canvas, pts, Scalar(255,255,255), 8, 0); std::vector<std::vector<Point>> contours; contours.push_back(pts); // 直接绘制并填充 drawContours(canvas, contours, -1, Scalar(255, 0, 0),-1); imshow("多边形绘制", canvas); } /********************************************************/ 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; std::cout << "start point:" << sp << std::endl; } else if (event == EVENT_LBUTTONUP) { ep.x = x; ep.y = y; int dx = ep.x - sp.x; int dy = ep.y - sp.y; if (dx > 0 && dy >> 0) { Rect box(sp.x, sp.y, dx, dy); imshow("ROI区域", image(box)); rectangle(image, box, Scalar(0,0,255), 2, 8, 0); imshow("鼠标绘制", image); // 为下次绘制做好准备 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 = ep.x - sp.x; int dy = ep.y - sp.y; if (dx > 0 && dy >> 0) { Rect box(sp.x, sp.y, dx, dy); temp.copyTo(image); rectangle(image, box, Scalar(0,0,255), 2, 8, 0); imshow("鼠标绘制", image); } } } } void QuickDemo::mouse_drawing_demo(Mat &image) { namedWindow("鼠标绘制", WINDOW_AUTOSIZE); setMouseCallback("鼠标绘制", on_draw, (void*)(&image)); imshow("鼠标绘制", image); temp = image.clone(); } void QuickDemo::norm_demo(Mat &image) { Mat dst; std::cout << image.type() << std::endl; // 16 - CV_8UC3 image.convertTo(image, CV_32F); std::cout << image.type() << std::endl; // 21 -CV_32FC3 normalize(image, dst, 1.0, 0, NORM_MINMAX); std::cout << dst.type() << std::endl; imshow("图像数据归一化", dst); } void QuickDemo::resize_demo(Mat &image) { Mat zoomin, zoomout; int h = image.rows; int w = image.cols; resize(image, zoomin, Size(w/2, h/2), 0, 0, INTER_LINEAR); imshow("zoomin", zoomin); resize(image, zoomout, Size(w*2, h*2), 0, 0, INTER_LINEAR); imshow("zoomax", zoomout); } 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; 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); warpAffine(image, dst, M, Size(nw, nh), INTER_LINEAR, 0, Scalar(0,0,255)); imshow("旋转演示", dst); } void QuickDemo::video_demo(Mat &image) { VideoCapture capture("/home/b/Downloads/opencv_tutorial_data-master/images/01.mp4"); int frame_width = capture.get(CAP_PROP_FRAME_WIDTH); int frame_height = capture.get(CAP_PROP_FRAME_HEIGHT); int count = capture.get(CAP_PROP_FRAME_COUNT); double fps = capture.get(CAP_PROP_FPS); std::cout << "frame_width:" << frame_width << std::endl; std::cout << "frame_height:" << frame_height << std::endl; std::cout << "number of frames:" << count << std::endl; std::cout << "fps:" << fps << std::endl; VideoWriter writer("./test.mp4", capture.get(CAP_PROP_FOURCC), fps, Size(frame_width, frame_height), true); Mat frame; while (true) { capture.read(frame); if (frame.empty()) { break; } imshow("frame", frame); writer.write(frame); int c = waitKey(10); if (c == 27) //退出 { break; } } capture.release(); writer.release(); } void QuickDemo::show_histogram_demo(Mat &image) { // 三通道分离 std::vector<Mat> bgr_plane; split(image, bgr_plane); // 定义参数变量 const int channels[1] = {0}; const int bins[1] = {256}; float hranges[2] = {0, 255}; const float *ranges[1] = {hranges}; Mat b_hist; Mat g_hist; Mat r_hist; // 计算B、G、R通道的直方图 calcHist(&bgr_plane[0], 1, 0, Mat(), b_hist, 1, bins, ranges); calcHist(&bgr_plane[1], 1, 0, Mat(), g_hist, 1, bins, ranges); calcHist(&bgr_plane[2], 1, 0, Mat(), r_hist, 1, bins, ranges); // 显示直方图 int hist_w = 512; int hist_h = 400; int bin_w = cvRound((double)hist_w / bins[0]); Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3); // 归一化直方图数据 normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat()); normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat()); normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat()); // 绘制直方图曲线 for (int i=0; i<bins[0]; i++) { line(histImage, Point(bin_w*(i-1), hist_h-cvRound(b_hist.at<float>(i-1))), \ Point(bin_w*(i), hist_h-cvRound(b_hist.at<float>(i))), Scalar(255,0,0), 2, 8, 0); line(histImage, Point(bin_w*(i-1), hist_h-cvRound(g_hist.at<float>(i-1))), \ Point(bin_w*(i), hist_h-cvRound(g_hist.at<float>(i))), Scalar(255,0,0), 2, 8, 0); line(histImage, Point(bin_w*(i-1), hist_h-cvRound(r_hist.at<float>(i-1))), \ Point(bin_w*(i), hist_h-cvRound(r_hist.at<float>(i))), Scalar(255,0,0), 2, 8, 0); } // 显示直方图 namedWindow("Histogram demo", WINDOW_AUTOSIZE); imshow("Histogram demo", histImage); } void QuickDemo::histogram_2d_demo(Mat &image) { } void QuickDemo::histogram_eq_demo(Mat &image) { } void QuickDemo::blur_demo(Mat &image) { Mat dst; blur(image, dst, Size(3,3), Point(-1,-1)); imshow("图像模糊", dst); } void QuickDemo::gaussain_blur_demo(Mat &image) { Mat dst; GaussianBlur(image, dst, Size(3,3), 15); imshow("高斯模糊", dst); } void QuickDemo::bifilter_demo(Mat &image) { Mat dst; bilateralFilter(image, dst, 0, 100, 10); imshow("高斯双边模糊", dst); }
quickopencv.h
#ifndef QUICKOPENCV_H__ #define QUICKOPENCV_H__ #include <opencv2/opencv.hpp> using namespace cv; class QuickDemo { public: // 02 图像色彩空间变换 void colorSpace_Demo(Mat &image); // 03 图像对象的创建与赋值 void mat_creation_demo(); // 04 图像像素的读写操作 void pixel_visit_demo(Mat &image); // 05 图像像素的算数操作 void operators_demo(Mat &image); // 06/07 滚动条操作演示 - 调整图像亮度 void tracking_bar_demo(Mat &image); // 08 键盘响应操作 void key_demo(Mat &image); // 09 opencv自带颜色表操作 void color_style_demo(Mat &image); // 10 图像像素的逻辑操作(位操作) void bitwise_demo(Mat &image); // 011 通道分离与合并 void channels_demo(Mat &image); // 012 图像色彩空间转换 void inrang_demo(Mat &image); // 013 图像像素值统计 void pixel_statistic_demo(Mat &image); // 014 画基本图形 void drawing_demo(Mat &image); // 015 随机数和随机颜色 void random_drawing_demo(); //016 多边形填充和绘制 void polyline_drawing_demo(); // 017 鼠标操作与响应, ROI在线提取 void mouse_drawing_demo(Mat &image); // 018 图像像素类型转换与归一化 void norm_demo(Mat &image); // 019 图像放缩与插值 void resize_demo(Mat &image); // 020 图像翻转 void flip_demo(Mat &image); // 021 图像旋转 任意角度旋转,并修改旋转后空白背景 void rotate_demo(Mat &image); // 022 视频文件摄像头调用 023 视频处理与保存 void video_demo(Mat &image); // 024 图像直方图 void show_histogram_demo(Mat &image); // 025 二维直方图 void histogram_2d_demo(Mat &image); // 026 直方图均衡化 void histogram_eq_demo(Mat &image); // 027 图像卷积操作 void blur_demo(Mat &image); // 028 高斯模糊 void gaussain_blur_demo(Mat &image); // 029 高斯双边模糊 void bifilter_demo(Mat &image); // 030 案例:实时人脸检测 }; #endif
test440.cpp
#include <opencv2/opencv.hpp> #include <quickopencv.h> #include <iostream> using namespace cv; using namespace std; int main(void) { Mat src = imread("./test.jpg"); if (src.empty()) { printf("cann't load image\n"); return -1; } imshow("xxxx", src); QuickDemo qd; // qd.colorSpace_Demo(src); // qd.mat_creation_demo(); // qd.pixel_visit_demo(src); // qd.operators_demo(src); // qd.tracking_bar_demo(src); // qd.key_demo(src); // qd.color_style_demo(src); // qd.bitwise_demo(src); // qd.channels_demo(src); // qd.inrang_demo(src); // qd.pixel_statistic_demo(src); // qd.drawing_demo(src); // qd.random_drawing_demo(); // qd.polyline_drawing_demo(); // qd.mouse_drawing_demo(src); // qd.norm_demo(src); // qd.resize_demo(src); // qd.flip_demo(src); // qd.rotate_demo(src); // qd.video_demo(src); // qd.show_histogram_demo(src); // qd.histogram_2d_demo(); // qd.histogram_eq_demo(src); // qd.blur_demo(src); // qd.gaussain_blur_demo(src); qd.bifilter_demo(src); waitKey(0); destroyAllWindows(); }
Makefile
CXX = g++ LIB = $(shell pkg-config --libs opencv4) INC += $(shell pkg-config --cflags opencv4) INC += -I ./ CXXFLAGS = -g -std=c++11 -Wall TARGET = main SRC=$(wildcard ./*.cpp) OBJ=$(patsubst %.cpp, %.o, $(SRC)) $(TARGET): $(OBJ) $(CXX) $(CXXFLAGS) -o $@ $^ $(LIB) $(OBJ):%.o: %.cpp $(CXX) $(CXXFLAGS) $(INC) -o $@ -c $< clean: rm -f *.o rm -f $(TARGET)