离职之际,自学一下图像相关的知识点,只是简单学了一点皮毛!


目录

一、 OpenCV环境搭建

二、使用opencv

常用接口说明

使用案例

1. 图像色彩空间转换

2. Mat对象的创建与赋值

3. 图像像素的读写操作

4. 图像像素的算术操作

5. 滚动条操作演示

6. 键盘响应操作

7. OpenCV自带颜色表操作

8. 图像像素的逻辑操作

9. 通道分离与合并

10. 图像色彩空间转换

11. 图像像素值统计

12. 图像几何形状绘制

13. 随机数与随机颜色

14. 多边形填充与绘制

15. 鼠标操作与响应

16. 图像像素类型转换与归一化

17. 图像缩放与插值

18. 图像翻转

19. 图像旋转

20. 视频文件摄像头使用

21. 图像卷积操作

22. 高斯模糊

23. 高斯双边模糊

三、键盘操作小demo

1. 键盘小demo

2. 旋转、缩放、裁剪、平移案例

3. 往图片中写入中文文字

四、总结


一、 OpenCV环境搭建

官网下载链接:Releases - OpenCV

下载后双击开始解压安装

解压完毕

进入路径 D:\opencv\opencv\build\x64\vc14\bin

vs2019及其以上的版本可以选择vc15那个文件夹

拷贝该路径,设置环境变量

在路径D:\opencv\opencv\build\x64\vc14\lib 会有项目中需要使用到的lib

新建一个VS工程,

右键 属性 - VC++目录 - 包含库目录,添加头文件路径进来

右键 属性 - VC++目录 - 库目录,添加lib文件路径进行

右键 属性 - 链接器 - 输入 - 附加依赖项,将opencv_world460d.lib添加进来,如果是release项目,那么就添加opencv_world460.lib

注意,属性中的“配置”和“平台”需要和界面的保持一致

当然,上面添加目录和库目录的操作,可以将相应的头文件和库拷贝到项目路径,再添加项目路径进来即可!

编写如下代码测试环境:

#include <opencv2/opencv.hpp>#include <iostream>
#include <stdio.h>using namespace cv;void int test1(Mat src) {// 设置新的窗体标题,参数一窗体标题,参数二窗体随图片大小namedWindow("opencv窗口2", WINDOW_AUTOSIZE);Mat output_image;/* 设置图片颜色 */cvtColor(src, output_image, COLOR_BGRA2RGB);imshow("opencv窗口2", output_image);/* 保存图片到本地 */imwrite("D:/new.png", output_image);// 等待waitKey(0);
}int main(void) {// 正常:IMREAD_COLOR// 灰度:IMREAD_GRAYSCALEMat image = imread("图.jpeg", IMREAD_COLOR);    // 读取一张图片if (image.empty()) {        // 判断图片是否读取成功printf("path image NULL!\n");return -1;}// 显示图片    参数一是窗体标题,参数二图片对象imshow("原图", image);test1(image);return 0;
}

编译运行,不出意外的话,就会显示两张我们指定路径读取的图片

左边那一种是修改颜色后的图片,右边是原图!

至此,环境搭建完毕!


二、使用opencv

常用接口说明

1. imread

Mat image = imread("图.jpeg", IMREAD_COLOR);

解析:读取一张图片。

2. image.empty()

解析:判断文件是否读取成功,ture表示不成功,false表示成功。

3. imshow

imshow("opencv窗口2", output_image);

解析:显示一张图片,参数一是窗体名字,参数二是图片对象,也就是Mat对象

4. imwrite

imwrite("D:/new.png", output_image);

解析:将图像保存到本地。参数一是文件路径名,参数二是图片对象。

5. waitKey

int waitKey(int delay = 0);

解析:等待,相当于c/c++中的system("pause"); 参数为0,表示一直等待,其他大于零的数,则表示等待对应的秒数。

6. destroyAllWindows

void destroyAllWindows();

解析:销毁所有打开的窗体。

7. cvtColor

void cvtColor( InputArray src, OutputArray dst, int code, int dstCn = 0 );

解析:设置图像的颜色。参数一是输入图像对象,参数二是设置好输出的图像对象,参数三是设置图像颜色枚举(enum ColorConversionCodes)

8. clone

Mat src = image.clone()

解析:将图像对象克隆赋值,需要另一个Mat对象去接收,相当于深拷贝。

8. copyTo

Mat m2;
image.copyTo(m2)

解析:将图像对象拷贝给另一个图像对象,相当于深拷贝。

9. zeros

Mat m4 = Mat::zeros(Size(128, 128), CV_8UC3);

解析:创建空白图像。参数一是图像大小,参数二表示通道数。

10. Scalar

m4 = Scalar(255, 255, 0);

解析:给图像设置颜色。因为是给三个通道的图像设置颜色,所以这里使用三个参数;如果是给单通道设置颜色,则设置一个参数即可。

11. cols

int width = m4.cols;

解析:获取图像的宽度。

12. rows

int height = m4.rows;

解析:获取图像的高度。

13. channels

int channels = m4.channels();

解析:获取通向的通道数。单通道表示灰色图像,三通道表示彩色图像。

14. add

add(image, m1, dst1);

解析:图像的像素相加。参数一和参数二是输入图像,参数三是输出图像。也就是将参数一和参数二的像素相加后输出到参数三。

15. subtract

subtract(image, m2, dst2);

解析:图像的像素相减。参数一和参数二是输入图像,参数三是输出图像。也就是将参数一和参数二的像素相减后输出到参数三。

16. multiply

multiply(image, m3, dst3);

解析:图像的像素相乘。参数一和参数二是输入图像,参数三是输出图像。也就是将参数一和参数二的像素相乘后输出到参数三。

17. divide

divide(image, m4, dst4);

解析:图像的像素相除。参数一和参数二是输入图像,参数三是输出图像。也就是将参数一和参数二的像素相除后输出到参数三。

18. waitKey

 int c = waitKey(100);

解析:等待键盘输入,如果参数设置的毫秒数结束后还没有键盘输入,则默认输入-1,结束等待。

19. applyColorMap

applyColorMap(image, dst, COLORMAP_COOL);

解析:设置图像彩色映射。参数一是输入图像,参数二是输出图像,参数三是映射的颜色类型,他是枚举enum ColormapTypes。

20. bitwise_and

bitwise_and(m1, m2, dst1);

解析:图像像素逻辑与操作。参数一和参数二是输入图像,参数三是输出图像。

21. bitwise_or

bitwise_or(m1, m2, dst2);

解析:图像像素逻辑或操作。参数一和参数二是输入图像,参数三是输出图像。

22. bitwise_not

bitwise_not(image, dst3);

解析:图像像素取反操作。参数一是输入图像,参数二是输出图像。

23. bitwise_xor

bitwise_xor(m1, m2, dst4);

解析:图像像素逻辑异或操作。参数一和参数二是输入图像,参数三是输出图像。

24. split

std::vector<Mat> mv;
split(image, mv);

解析:通道分离。

25. rectangle

rectangle(dst1, rect, Scalar(0, 255, 255), 2, 8, 0);

解析:绘制一个矩形。参数一是输出图像,参数二是矩形对象,参数三是颜色,参数四是边框厚度,参数五是反锯齿(可以使用LINE_AA效果更加),参数六不清楚。

如果是需要填充,则将参数四传入-1。

26. circle

circle(dst1, Point(300, 400), 30, Scalar(255, 0, 255), 2, 8, 0);

解析:绘制一个圆形。参数一是输出图像,参数二是圆的中心位置,参数三是圆的半径,参数四是颜色,参数五是边框厚度,参数六是反锯齿(可以使用LINE_AA效果更加),参数七不清楚。

如果是需要填充,则将参数五传入-1。

27. line

line(dst5, Point(100, 130), Point(300, 400), Scalar(255, 0, 0), 2, 8, 0);

解析:绘制一条线。参数一是输入输出图像, 参数二是第一个点位置,参数三是第二个点位置,参数四是颜色,参数五是线的宽度,参数六是反锯齿,参数七不清楚。

28. RNG

RNG rng((unsigned)time(NULL));    // 用系统时间作为种子初始化rng,要用到time.h库
int b = rng.uniform(0, 255);    // 随机生成 0 - 255

解析:opencv的随机函数。

29. drawContours

drawContours(canvas, contours, 0, Scalar(255, 0, 0), -1);

解析:绘制多边形或者填充多边形。参数一是输入输出图像,参数二是std::vector<std::vector<Point>>类型对象,参数三指定绘制容器中指定数据的第几个(-1表示全都绘制),最后一个参数为多边形的边框(-1表示填充)

30. resize

resize(image, zoomin, Size(w / 2, h / 2));

解析:图像缩放。参数一是输入图像,参数二是输出图像,参数三是重新设置的图像大小。

31. flip

// 上下翻转
flip(image, dst, 0);// 左右翻转
flip(image, dst, 1);// 先左右翻转再上下翻转
flip(image, dst, -1);

解析:图像翻转。参数一是输入图像,参数二是输出图像,参数三0表示上下翻转,1表示左右翻转,-1表示先左右翻转再上下翻转。

使用案例

以下函数的参数都基于此读取的图像对象:

int main(void) {// 正常:IMREAD_COLOR// 灰度:IMREAD_GRAYSCALEMat image = imread("图.jpeg", IMREAD_COLOR);if (image.empty()) {printf("path image NULL!\n");return -1;}imshow("原图", image);waitKey(0);destroyAllWindows();return 0;
}

1. 图像色彩空间转换

/* 图像色彩空间转换 */
void colorSpace_Demo(Mat &image) {Mat gray, hsv;cvtColor(image, hsv, COLOR_BGR2HSV);cvtColor(image, gray, COLOR_BGR2GRAY);  // 灰度图像imshow("HSV", hsv);imshow("灰度", gray);imwrite("HSV.png", hsv);imwrite("GRAY.png", gray);
}

运行截图

2. Mat对象的创建与赋值

/* Mat对象的创建与赋值 */
void mat_creation_demo(Mat &image) {Mat m1, m2, m3;// 克隆m1 = image.clone();        // 深拷贝// 拷贝image.copyTo(m2);        // 深拷贝// 赋值m3 = image;             // 浅拷贝// 创建空白图像//Mat m4 = Mat::zeros(Size(8, 8), CV_8UC1);     // 创建一通道Mat m4 = Mat::zeros(Size(128, 128), CV_8UC3);  // 最后的数字表示通道数// 给通道赋值(相当于设置颜色)m4 = Scalar(123, 200, 100);    // 因为是3通道,所以赋三个值;如果是一通道,给一个参数值即可//std::cout << m4 << std::endl;imshow("m4", m4);// 图像的宽int width = m4.cols;// 图像的高int height = m4.rows;// 图像的通道数int channels = m4.channels();printf("width = %d\theight = %d\tchannels = %d\n", width, height, channels);// C++11新出的创建空白图像方法Mat kernel = (Mat_<char>(3, 3) <<0, -1, 0,-1, 5, -1,0, -1, 0);kernel = Scalar(50, 100, 150);imshow("kernel", kernel);
}

运行截图

3. 图像像素的读写操作

/* 图像像素的读写操作 */
void 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 (1 == dims) {//            // 获取通道中的值//            int pv = image.at<uchar>(row, col);//            // 还可以对其进行修改//          image.at<uchar>(row, col) = 255 - pv;    // 得确保值在0-255区间//       } else if (3 == dims) {   // 三通道 - 彩色图像//         // 获取通道中的三个值//          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 (1 == dims) {*current_row++ = 255 - *current_row;} else if (3 == dims) {*current_row++ = 255 - *current_row;*current_row++ = 255 - *current_row;*current_row++ = 255 - *current_row;}}}imshow("pixel_visit_demo", image);
}

运行截图:

4. 图像像素的算术操作

/* 图像像素的算术操作 */
void operators_demo(Mat &image) {// 加Mat dst1;Mat m1 = Mat::zeros(image.size(), image.type());m1 = Scalar(100, 100, 100); // 给通道赋值(相当于设置颜色)add(image, m1, dst1);imshow("图像像素加法操作", dst1);// 减Mat dst2;Mat m2 = Mat::zeros(image.size(), image.type());m2 = Scalar(50, 50, 50);subtract(image, m2, dst2);imshow("图像像素减法操作", dst2);// 乘Mat dst3;Mat m3 = Mat::zeros(image.size(), image.type());m3 = Scalar(2, 2, 2);multiply(image, m3, dst3);   // 参数一乘以参数二结果给参数三imshow("图像像素乘法操作", dst3);// 除Mat dst4;Mat m4 = Mat::zeros(image.size(), image.type());m4 = Scalar(5, 5, 5);divide(image, m4, dst4);imshow("图像像素除法操作", dst4);///* 另外一种方式实现加法操作 *///Mat dst5 = Mat::zeros(image.size(), image.type());//Mat m5 = Mat::zeros(image.size(), image.type());//m5 = Scalar(50, 50, 50);//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 (3 == dims) {//            Vec3b p1 = image.at<Vec3b>(row, col);//          Vec3b p2 = m5.at<Vec3b>(row, col);//         // 加减乘除都是一样的//          dst5.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(p1[0] + p2[0]);//          dst5.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(p1[1] + p2[1]);//          dst5.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(p1[2] + p2[2]);//      }//     //  }//}//imshow("图像像素加法操作", dst5);
}

运行截图

5. 滚动条操作演示

static void on_track(int b, void *userdata) {Mat image = *((Mat*)userdata);Mat dst = Mat::zeros(image.size(), image.type());Mat m = Mat::zeros(image.size(), image.type());m = Scalar(b, b, b);add(image, m, dst);imshow("亮度调整", dst);
}
/* 滚动条操作演示 */
void tracking_bar_demo(Mat &image) {namedWindow("亮度调整", WINDOW_AUTOSIZE);int lightness = 0;int max_value = 100;// 创建一个滚动条createTrackbar("Value Bar:", "亮度调整", &lightness, max_value, on_track, (void*)(&image));on_track(lightness, &image);
}

运行截图

6. 键盘响应操作

/* 键盘响应操作 */
void key_demo(Mat &image) {bool flag = true;Mat input;image.copyTo(input);while (flag) {int c = waitKey(100); // 等待键盘输入,等待100msprintf("按键:%d\n", c);switch (c) {case 27: {            // Esc退出flag = false;}break;case 52: {         // 4 向左旋转}break;case 54: {          // 6 向右旋转}break;case 56: {          // 8 放大}break;case 50: {            // 2 缩小}break;case 48: {        // 0 复原}break;default:              break;}imshow("原图", image);}
}

这个键盘响应,我用来写了一个小demo,通过键盘按键来对图片进行旋转、缩放、平移、翻转和裁剪的操作。

7. OpenCV自带颜色表操作

/* OpenCV自带颜色表操作 */
void color_style_demo(Mat &image) {int colorMap[] = {COLORMAP_AUTUMN,COLORMAP_BONE,COLORMAP_CIVIDIS,COLORMAP_COOL,COLORMAP_DEEPGREEN,COLORMAP_HOT,COLORMAP_HSV,COLORMAP_INFERNO,COLORMAP_JET,COLORMAP_MAGMA,COLORMAP_OCEAN,COLORMAP_PARULA,COLORMAP_PINK,COLORMAP_PLASMA,COLORMAP_RAINBOW,COLORMAP_SPRING,COLORMAP_SUMMER,COLORMAP_TURBO,COLORMAP_TWILIGHT,COLORMAP_TWILIGHT_SHIFTED,COLORMAP_VIRIDIS,COLORMAP_WINTER,DECOMP_NORMAL};Mat dst;int index = 0;while (1) {int c = waitKey(1000);if (27 == c) {break;}applyColorMap(image, dst, colorMap[index % 19]);index++;imshow("Opencv自带颜色表", dst);}
}

数组colorMap中存储的,都是enum ColormapTypes枚举中的数据。

运行截图

8. 图像像素的逻辑操作

/* 图像像素的逻辑操作 */
void 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;bitwise_and(m1, m2, dst1);imshow("逻辑与", dst1);// 逻辑或Mat dst2;bitwise_or(m1, m2, dst2);imshow("逻辑或", dst2);// 逻辑非Mat dst3;bitwise_not(image, dst3);imshow("逻辑非", dst3);// 逻辑异或Mat dst4;bitwise_xor(m1, m2, dst4);imshow("逻辑异或", dst4);
}

运行截图

9. 通道分离与合并

/* 通道分离与合并 */
void channels_demo(Mat &image) {std::vector<Mat> mv;// 将一个多通道数组划分为多个单通道数组split(image, mv);imshow("blue", mv.at(0));imshow("green", mv.at(1));imshow("red", mv.at(2));Mat dst;//mv.at(1) = 0;mv.at(2) = 0;// 通道合并merge(mv, dst);imshow("合并通道颜色", dst);int from_to[] = { 0, 2, 1, 1, 2, 0 }; // 将0 和 2通道交换// 通道混合mixChannels(&image, 1, &dst, 1, from_to, 3);imshow("通道混合", dst);
}

运行截图

10. 图像色彩空间转换

/* 图像色彩空间转换 */
void inrange_demo(Mat &image) {Mat hsv;// 转换位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(40, 40, 200);   // 红色背景图bitwise_not(mask, mask);        // 逻辑非imshow("mask", mask);image.copyTo(redback, mask);   // 将原图草莓图中按照mask里白色区域拷贝到参数一中imshow("roi区域提取", redback);
}

运行截图

11. 图像像素值统计

/* 图像像素值统计 */
void 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());printf("No.channels:%d\tminvalue:%f\tmaxvalue:%f\n", i, minv, maxv);}Mat mean, stddev;// 计算矩阵的均值和标准偏差meanStdDev(image, mean, stddev);std::cout << "means: " << mean << std::endl;std::cout << "stddev:" << stddev << std::endl;
}

运行截图

12. 图像几何形状绘制

/* 图像几何形状绘制 */
void drawing_demo(Mat &image) {Rect rect;rect.x = 100;rect.y = 130;rect.width = 200;rect.height = 270;Mat dst1 = image.clone();Mat dst2 = image.clone();// 绘制一个矩形rectangle(dst1, rect, Scalar(0, 255, 255), 2, 8, 0);// 绘制一个圆形circle(dst1, Point(300, 400), 30, Scalar(255, 0, 255), 2, 8, 0);imshow("绘制演示", dst1);// 填充一个矩形rectangle(dst2, rect, Scalar(255, 255, 0), -1, 8, 0);// 填充一个圆形circle(dst2, Point(100, 400), 30, Scalar(0, 255, 0), -1, 8, 0);imshow("填充演示", dst2);Mat dst5 = image.clone();// 绘制线line(dst5, Point(100, 130), Point(300, 400), Scalar(255, 0, 0), 2, 8, 0);// 绘制椭圆RotatedRect rrt;rrt.center = Point(200, 200);rrt.size = Size(100, 200);rrt.angle = 90.0;ellipse(dst5, rrt, Scalar(0, 0, 255), 2, 8); // 如果需要填充,第四个参数传-1imshow("线和椭圆", dst5);Mat dst3, dst4;dst3 = Mat::zeros(image.size(), image.type());dst3 = Scalar(100, 180, 200);imshow("dst3", dst3);// 将两张相同大小,相同类型的图片融合的函数addWeighted(image, 0.7, dst3, 0.5, 1, dst4);imshow("两种图片融合", dst4);
}

运行截图

13. 随机数与随机颜色

/* 随机数与随机颜色 */
void random_drawing_demo() {Mat canvas = Mat::zeros(Size(512, 512), CV_8UC3);int w = canvas.cols;int h = canvas.rows;RNG rng((unsigned)time(NULL)); // 用系统时间作为种子初始化rng,要用到time.h库while (1) {int c = waitKey(10);if (27 == c) {break;}canvas = Scalar(0, 0, 0);// 获得随机值int x1 = rng.uniform(0, w);int y1 = rng.uniform(0, h);int x2 = rng.uniform(0, w);int y2 = rng.uniform(0, h);int b = rng.uniform(0, 255);int g = rng.uniform(0, 255);int r = rng.uniform(0, 255);line(canvas, Point(x1, y1), Point(x2, y2), Scalar(b, g, r), 1, LINE_AA, 0);imshow("随机数与随机颜色", canvas);}
}

运行截图

14. 多边形填充与绘制

/* 多边形填充与绘制 */
void polyline_drawing_demo() {Mat canvas = Mat::zeros(Size(512, 512), CV_8UC3);Point p1(100, 100);Point p2(350, 120);Point p3(300, 350);Point p4(450, 500);Point p5(50, 200);std::vector<Point> pts;pts.emplace_back(p1);pts.emplace_back(p2);pts.emplace_back(p3);pts.emplace_back(p4);pts.emplace_back(p5);// 填充一个多边形//fillPoly(canvas, pts, Scalar(255, 0, 255), 8, 0);// 绘制一个多边形//polylines(canvas, pts, true, Scalar(0, 0, 255), 2, LINE_AA, 0);std::vector<std::vector<Point>> contours;contours.push_back(pts);// 绘制多边形或者填充多边形,参数三指定绘制容器中指定数据的第几个(-1表示全都绘制),最后一个参数为多边形的边框(-1表示填充)drawContours(canvas, contours, 0, Scalar(255, 0, 0), -1);imshow("多边形填充与绘制",  canvas);
}

运行截图

15. 鼠标操作与响应

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 && ep.y < image.size().height && ep.x < image.size().width) {Rect box(sp.x, sp.y, dx, dy);rectangle(image, box, Scalar(0, 0, 255), 2, 8, 0);    // 绘制矩形imshow("鼠标绘制", image);imshow("ROI区域提取", temp(box));  // 提取矩形中的图像} else {temp.copyTo(image);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) {temp.copyTo(image);Rect box(sp.x, sp.y, dx, dy);rectangle(image, box, Scalar(0, 0, 255), 2, 8, 0);imshow("鼠标绘制", image);}}}
}
/* 鼠标操作与响应 */
void mouse_drawing_demo(Mat &image) {namedWindow("鼠标绘制", WINDOW_AUTOSIZE);// 设置鼠标回调setMouseCallback("鼠标绘制", on_draw, (void*)(&image));imshow("鼠标绘制", image);temp = image.clone();
}

运行截图

16. 图像像素类型转换与归一化

/* 图像像素类型转换与归一化 */
void norm_demo(Mat &image) {Mat dst;std::cout << image.type() << std::endl;image.convertTo(image, CV_32F);std::cout << image.type() << std::endl;normalize(image, dst, 1.0, 0, NORM_MINMAX);std::cout << dst.type() << std::endl;imshow("图像数据归一化", dst);
}

运行截图

17. 图像缩放与插值

/* 图像缩放与插值 */
void 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*1.5, h*1.5), 0, 0, INTER_LINEAR);imshow("zoomout", zoomout);
}

运行截图

18. 图像翻转

/* 图像翻转 */
void flip_demo(Mat &image) {Mat dst;// 上下翻转flip(image, dst, 0);// 左右翻转//flip(image, dst, 1);// 先左右翻转再上下翻转//flip(image, dst, -1);imshow("图像翻转", dst);
}

运行截图

19. 图像旋转

/* 图像旋转 */
void 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(255, 0, 0));imshow("图像旋转", dst);
}

运行截图

20. 视频文件摄像头使用

/* 视频文件摄像头使用 */
void video_demo() {// VideoCapture capture(0);      // 参数是0,就是读取电脑摄像头VideoCapture capture("E:\\Code\\vs2017Code\\OpencvTest2\\OpencvTest2\\win10.mp4");    // 视频宽int frame_width = capture.get(CAP_PROP_FRAME_WIDTH);// 视频高int frame_hegiht = capture.get(CAP_PROP_FRAME_HEIGHT);// 视频总帧率int count = capture.get(CAP_PROP_FRAME_COUNT);// 视频每秒多少fps(每秒多少帧)double fps = capture.get(CAP_PROP_FPS);std::cout << "froma_width:" << frame_width << std::endl;std::cout << "frame_hegiht:" << frame_hegiht << std::endl;std::cout << "count:" << count << std::endl;std::cout << "fps:" << fps << std::endl;// 定义视频保存对象VideoWriter writer("D://test.mp4", capture.get(CAP_PROP_FOURCC), fps, Size(frame_width, frame_hegiht), true);Mat frame;while (1) {capture.read(frame);   // 读取画面//flip(frame, frame, 1); // 左右翻转,读取电脑摄像头时需要翻转,视频不用if (frame.empty()) {break;}imshow("frame", frame);// 保存视频writer.write(frame);int c = waitKey(10);if (27 == c) {break;}}// 释放capture.release();writer.release();
}

运行截图

21. 图像卷积操作

/* 图像卷积操作 */
void blur_demo(Mat &image) {Mat dst;blur(image, dst, Size(1, 1), Point(-1, -1));    // // 设置参数三的数值即可imshow("图像模糊", dst);
}

运行截图

22. 高斯模糊

/* 高斯模糊 */
void gaussian_blur_demo(Mat &image) {Mat dst;GaussianBlur(image, dst, Size(0, 0), 5);   // 参数三默认0即可,设置参数四实现模糊imshow("高斯模糊", dst);
}

运行截图

23. 高斯双边模糊

/* 高斯双边模糊 */
void bifilter_demo(Mat &image) {Mat dst;bilateralFilter(image, dst, 0, 100, 10);imshow("双边模糊", dst);
}

运行截图


三、键盘操作小demo

1. 键盘小demo

对图片操作,旋转,缩放,平移,翻转,裁剪的操作。

#include <opencv2/opencv.hpp>using namespace cv;void imageOperate1(Mat &image, Mat input, double angle, double scale, int xOffset, int yOffset, int startRow, int &endRow, int startCol, int &endCol, bool up_down, bool left_right) {input.copyTo(image);// 图像平移Size dst_sz = image.size();//定义平移矩阵Mat t_mat = Mat::zeros(2, 3, CV_32FC1);t_mat.at<float>(0, 0) = 1;t_mat.at<float>(0, 2) = xOffset; //水平平移量t_mat.at<float>(1, 1) = 1;t_mat.at<float>(1, 2) = yOffset; //竖直平移量//根据平移矩阵进行仿射变换warpAffine(image, image, t_mat, dst_sz);// 旋转图像Point2f center((image.cols - 1) / 2.0, (image.rows - 1) / 2.0);  // 图像中心位置Mat rotation_matix = getRotationMatrix2D(center, angle, 1.0);warpAffine(image, image, rotation_matix, image.size());// 缩放图像int width = int(input.cols * scale);int height = int(input.rows * scale);resize(image, image, cv::Size(width, height), 0, 0, INTER_LINEAR);if (0 == endRow) {      // 发生了缩放操作endRow = height;endCol = width;}// 裁剪图像 image = image(Range(startRow, endRow), Range(startCol, endCol));// 上下翻转if (up_down) flip(image, image, 0);// 左右翻转if (left_right) flip(image, image, 1);
}
/* 键盘响应操作 */
void key_demo1(Mat &image) {bool flag = true;double scale = 1.0;      // 缩放比例double angle = 0;       // 旋转角度int xOffset = 0;        // 水平平移量int yOffset = 0;       // 竖直平移量int startRow = 0;                  // 裁剪起始行int endRow = image.size().height;  // 裁剪结束行int startCol = 0;                  // 裁剪起始列int endCol = image.size().width;   // 才接结束列bool up_down = false;              // 上下翻转bool left_right = false;            // 左右翻转Mat input;image.copyTo(input);while (flag) {int c = waitKey(100);   // 等待键盘输入,等待100msprintf("按键:%d\n", c);switch (c) {case 27:{         // Esc退出flag = false;}break;case 52:{          // 4 向左旋转angle += 5;imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);}break;case 54:{         // 6 向右旋转angle -= 5;imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);}break;case 56:{          // 8 放大scale += 0.1;if (scale <= 2.05) {  // 设置最大放大是2endRow = 0;imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);} else {scale = 2.0;}}break;case 50:{          // 2 缩小scale -= 0.1;if (scale >= 0.15) {   // 最小缩小是0.2endRow = 0;imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);} else {scale = 0.2;}}break;case 87:case 119:{     // wW 图片上移yOffset -= 2;imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);}break;case 83:case 115:{      // sS 图片下移yOffset += 2;imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);}break;case 65:case 97:{      // aA 图片左移xOffset -= 2;imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);}break;case 68:case 100:{      // dD 图片右移xOffset += 2;imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);}break;case 73:{      // I 图片上边复原if (startRow > 0) {startRow -= 1;imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);} else {startRow = 0;}}break;case 105:{       // i 图片上边裁剪if (startRow < endRow - 1) {startRow += 1;imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);} else {startRow = endRow - 1;}}break;case 75:{     // K 图片下边复原if (endRow < input.size().height) {endRow += 1;imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);} else {endRow = input.size().height;}}break;case 107:{        // k 图片下边裁剪if (endRow > startRow + 1) {endRow -= 1;imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);} else {endRow = startRow + 1;}}break;case 74:{      // J 图片左边恢复if (startCol > 0) {startCol -= 1;imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);} else {startCol = 0;}}break;case 106:{       // j 图片左边裁剪if (startCol < endCol - 1) {startCol += 1;imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);} else {startCol = endCol - 1;}}break;case 76:{     // L 图片右边复原if (endCol < input.size().width) {endCol += 1;imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);} else {endCol = input.size().width;}}break;case 108:{      // l 图片右边裁剪if (endCol > startCol + 1) {endCol -= 1;imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);} else {endCol = startCol + 1;}}break;case 43:{      // + 上下翻转up_down = !up_down;imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);}break;case 45:{     //  - 左右翻转left_right = !left_right;imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);}break;case 48:{       // 0 复原scale = 1.0;                    // 缩放比例angle = 0;                      // 旋转角度xOffset = 0;                    // 水平平移量yOffset = 0;                   // 竖直平移量startRow = 0;                  // 裁剪起始行endRow = input.size().height;  // 裁剪结束行startCol = 0;                  // 裁剪起始列endCol = input.size().width;   // 才接结束列up_down = false;               // 上下翻转left_right = false;             // 左右翻转imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);}break;default:break;}imshow("原图", image);}
}int main(void) {Mat image = imread("图.jpeg", IMREAD_COLOR);if (image.empty()) {printf("path image NULL!\n");return -1;}imshow("原图", image);key_demo1(image);waitKey(0);destroyAllWindows();return 0;
}

运行截图

2. 旋转、缩放、裁剪、平移案例

#include <stdio.h>
#include <Windows.h>
#include "opencv2/opencv.hpp"using namespace cv;// 图片旋转
Mat imageRotate(Mat input, double angle) {angle *= -1;// get the center coordinates of the image to create the 2D rotation matrixPoint2f center((input.cols - 1) / 2.0, (input.rows - 1) / 2.0);// using getRotationMatrix2D() to get the rotation matrixMat rotation_matix = getRotationMatrix2D(center, angle, 1.0);Mat newImage;warpAffine(input, newImage, rotation_matix, input.size());//namedWindow("测试图片旋转", WINDOW_FREERATIO);//imshow("测试图片旋转", newImage);imshow("测试显示图片", newImage);return newImage;
}// 图片缩放
Mat imageZoom(Mat input, float scaleW, float scaleH) {//float scaleW = 0.5; // 定义新图像的大小,宽度缩小到80%//float scaleH = 0.5;  //定义新图像的大小,高度缩小到80%int width = int(input.cols * scaleW);//定义想要扩大或者缩小后的宽度,src.cols为原图像的宽度,乘以80%则得到想要的大小,并强制转换成int型int height = int(input.rows * scaleH);//定义想要扩大或者缩小后的高度,src.cols为原图像的高度,乘以80%则得到想要的大小,并强制转换成int型cv::Mat dst;resize(input, dst, cv::Size(width, height));//缩放图像//namedWindow("测试图片缩放", WINDOW_FREERATIO);//imshow("测试图片缩放", dst);imshow("测试显示图片", dst);return dst;
}// 图片裁剪
Mat imageCut(Mat input, int startRow, int endRow, int startCol, int endCol) {Mat cropped_image = input(Range(startRow, endRow), Range(startCol, endCol));//namedWindow("测试裁剪图片", WINDOW_FREERATIO);//imshow("测试裁剪图片", cropped_image);imshow("测试显示图片", cropped_image);return cropped_image;
}// 图像平移
Mat imageTranslation(Mat srcImage, int xOffset, int yOffset)
{Size dst_sz = srcImage.size();//定义平移矩阵Mat t_mat = Mat::zeros(2, 3, CV_32FC1);t_mat.at<float>(0, 0) = 1;t_mat.at<float>(0, 2) = xOffset; //水平平移量t_mat.at<float>(1, 1) = 1;t_mat.at<float>(1, 2) = yOffset; //竖直平移量//根据平移矩阵进行仿射变换Mat TranslationMat;warpAffine(srcImage, TranslationMat, t_mat, dst_sz);//namedWindow("测试平移图片", WINDOW_FREERATIO);//imshow("测试平移图片", TranslationMat);imshow("测试显示图片", TranslationMat);return TranslationMat;
}int main(void) {Mat image =imread("123.png");//namedWindow("测试显示图片", WINDOW_FREERATIO);   // WINDOW_FREERATIO:可以手动调节大小//imshow("测试显示图片", image);//waitKey(0);double angle = 120;Mat imgRotate = imageRotate(image, angle);waitKey(0);float scale = 0.5;Mat imgZoom = imageZoom(imgRotate, scale, scale);waitKey(0);Mat imgCut = imageCut(imgZoom, 50, 80, 200, 300);waitKey(0);imageTranslation(imgCut, 10, 20);waitKey(0);destroyAllWindows();return 0;
}

3. 往图片中写入中文文字

记录一个类型转换:

// char* 转 wchar_t*
    wchar_t wcha[255] = { 0 };
    MultiByteToWideChar(CP_ACP, 0, text, strlen(text), wcha, 255);

putTextCN.h

#pragma once
#include <windows.h>
#include <string>
#include <opencv2/opencv.hpp>using namespace cv;// 内部调用
static void GetStringSize(HDC hDC, const char* str, int* w, int* h);
// 内部调用
static void putTextZH(Mat dst, const char* str, Point org, Scalar color, int fontSize,const char *fn, bool italic, bool underline, bool lfWeight);// 外部调用,往图片中写入文字
extern void putTextZh(InputOutputArray     img,const char *text,Point     org,int     fontSize,Scalar     color,const char *font = "Arial",bool italic = false,bool underline = false,bool lfWeight = false);

putTextCN.cpp

#include "putTextCN.h"static void GetStringSize(HDC hDC, const char* str, int* w, int* h) {SIZE size;GetTextExtentPoint32A(hDC, str, strlen(str), &size);if (w != 0) *w = size.cx;if (h != 0) *h = size.cy;
}static void putTextZH(Mat dst, const char* str, Point org, Scalar color, int fontSize, const char* fn, bool italic, bool underline, bool lfWeight) {CV_Assert(dst.data != 0 && (dst.channels() == 1 || dst.channels() == 3));int x, y, r, b;if (org.x > dst.cols || org.y > dst.rows) return;x = org.x < 0 ? -org.x : 0;y = org.y < 0 ? -org.y : 0;LOGFONTA lf;lf.lfHeight = -fontSize;lf.lfWidth = 0;lf.lfEscapement = 0;lf.lfOrientation = 0;if (true == lfWeight) {    // 粗体lf.lfWeight = 700;} else {lf.lfWeight = 400;}    lf.lfItalic = italic;       // 斜体lf.lfUnderline = underline; // 下划线lf.lfStrikeOut = 0;lf.lfCharSet = DEFAULT_CHARSET;lf.lfOutPrecision = 0;lf.lfClipPrecision = 0;lf.lfQuality = PROOF_QUALITY;lf.lfPitchAndFamily = 0;strcpy_s(lf.lfFaceName, fn);HFONT hf = CreateFontIndirectA(&lf);HDC hDC = CreateCompatibleDC(0);HFONT hOldFont = (HFONT)SelectObject(hDC, hf);int strBaseW = 0, strBaseH = 0;int singleRow = 0;char buf[1 << 12];strcpy_s(buf, str);char *bufT[1 << 12];  // 这个用于分隔字符串后剩余的字符,可能会超出。//处理多行{int nnh = 0;int cw, ch;const char* ln = strtok_s(buf, "\n", bufT);while (ln != 0) {GetStringSize(hDC, ln, &cw, &ch);strBaseW = max(strBaseW, cw);strBaseH = max(strBaseH, ch);ln = strtok_s(0, "\n", bufT);nnh++;}singleRow = strBaseH;strBaseH *= nnh;}if (org.x + strBaseW < 0 || org.y + strBaseH < 0) {SelectObject(hDC, hOldFont);DeleteObject(hf);DeleteObject(hDC);return;}r = org.x + strBaseW > dst.cols ? dst.cols - org.x - 1 : strBaseW - 1;b = org.y + strBaseH > dst.rows ? dst.rows - org.y - 1 : strBaseH - 1;org.x = org.x < 0 ? 0 : org.x;org.y = org.y < 0 ? 0 : org.y;BITMAPINFO bmp = { 0 };BITMAPINFOHEADER& bih = bmp.bmiHeader;int strDrawLineStep = strBaseW * 3 % 4 == 0 ? strBaseW * 3 : (strBaseW * 3 + 4 - ((strBaseW * 3) % 4));bih.biSize = sizeof(BITMAPINFOHEADER);bih.biWidth = strBaseW;bih.biHeight = strBaseH;bih.biPlanes = 1;bih.biBitCount = 24;bih.biCompression = BI_RGB;bih.biSizeImage = strBaseH * strDrawLineStep;bih.biClrUsed = 0;bih.biClrImportant = 0;void* pDibData = 0;HBITMAP hBmp = CreateDIBSection(hDC, &bmp, DIB_RGB_COLORS, &pDibData, 0, 0);CV_Assert(pDibData != 0);HBITMAP hOldBmp = (HBITMAP)SelectObject(hDC, hBmp);//color.val[2], color.val[1], color.val[0]SetTextColor(hDC, RGB(255, 255, 255));SetBkColor(hDC, 0);//SetStretchBltMode(hDC, COLORONCOLOR);strcpy_s(buf, str);const char* ln = strtok_s(buf, "\n", bufT);int outTextY = 0;while (ln != 0) {TextOutA(hDC, 0, outTextY, ln, strlen(ln));outTextY += singleRow;ln = strtok_s(0, "\n", bufT);}uchar* dstData = (uchar*)dst.data;int dstStep = dst.step / sizeof(dstData[0]);unsigned char* pImg = (unsigned char*)dst.data + org.x * dst.channels() + org.y * dstStep;unsigned char* pStr = (unsigned char*)pDibData + x * 3;for (int tty = y; tty <= b; ++tty) {unsigned char* subImg = pImg + (tty - y) * dstStep;unsigned char* subStr = pStr + (strBaseH - tty - 1) * strDrawLineStep;for (int ttx = x; ttx <= r; ++ttx) {for (int n = 0; n < dst.channels(); ++n) {double vtxt = subStr[n] / 255.0;int cvv = vtxt * color.val[n] + (1 - vtxt) * subImg[n];subImg[n] = cvv > 255 ? 255 : (cvv < 0 ? 0 : cvv);}subStr += 3;subImg += dst.channels();}}SelectObject(hDC, hOldBmp);SelectObject(hDC, hOldFont);DeleteObject(hf);DeleteObject(hBmp);DeleteDC(hDC);
}extern void putTextZh(InputOutputArray     img,const char *text,Point     org,int     fontSize,Scalar     color,const char  *font,bool italic,bool underline, bool lfWeight) {Mat src = img.getMat();putTextZH(src, text, org, color, fontSize, font, italic, underline, lfWeight);
}

main.cpp

#include "putTextCN.h"/* 绘制文字 */
void putText_demo(Mat &image, char *text) {putTextZh(image, text, Point(150, 50), 20, Scalar(0, 0, 0), "宋体");imshow("绘制文字", image);
}int main(void) {// 正常:IMREAD_COLOR// 灰度:IMREAD_GRAYSCALEMat image = imread("图.jpeg", IMREAD_COLOR);if (image.empty()) {printf("path image NULL!\n");return -1;}imshow("原图", image);char str[] = "测试OpenCV绘制文字";putText_demo(image, str);waitKey(0);destroyAllWindows();return 0;
}

运行截图


四、总结

opencv对图片一些简单的操作,我也就只会这些简单的了,也是在B站看视频学习后记录下来的代码,具体其他高深的的操作就不会了!

C/C++ vs2017 OpenCV简单入门相关推荐

  1. 免费送书啦!《 OpenCV图像处理入门与实践》一本全搞定

    OpenCV 的基础图像操作都只是针对图像中的像素点,并不是直接对图像整体进行的操作.而很多时候并不能仅通过改变像素点来进行图像的操作,为此我们需要学习关于图像的算术操作. 1.图像加法 对于两张相同 ...

  2. OpenCV C++入门,读取和显示一张图片

    文章目录 一.OpenCV C++入门,读取和显示一张图片 二.代码如下 三.类和函数介绍 一.OpenCV C++入门,读取和显示一张图片 OpenCV入门程序,简单显示一张图片. 二.代码如下 # ...

  3. OpenCV基础入门【C++及python语言】

    OpenCV基础入门[C++语言] OpenCV-Python 中文教程 OpenCV官方教程中文版(For Python) OpenCV2-Python-Tutorials 部分文件参考: http ...

  4. 【OpenCV图像处理入门学习教程六】基于Python的网络爬虫与OpenCV扩展库中的人脸识别算法比较

    OpenCV图像处理入门学习教程系列,上一篇第五篇:基于背景差分法的视频目标运动侦测 一.网络爬虫简介(Python3) 网络爬虫,大家应该不陌生了.接下来援引一些Jack-Cui在专栏<Pyt ...

  5. OpenCV快速入门篇(Python实现)

    OpenCV快速入门篇(Python实现) 转载自:https://blog.csdn.net/feilong_csdn/article/details/82750029 本系列python版本:py ...

  6. 小白opencv的入门处理技巧

    小白opencv的入门处理技巧 在这里插入代码片@TOC** 在这里插入代码片 import cv2 import numpy as np #i=cv2.imread("D:\\TP\\MT ...

  7. OpenCV基础入门系列基本操作——贰

    系列博文第二篇,关于OpenCV4的一些基本操作和使用. 博文主要以实例展示不同的函数使用方法. OpenCV基础入门系列基本操作--壹 前言 下述为本博文需要用到的各类头文件以及全局变量等 读者可根 ...

  8. opencv快速入门人脸检测与人脸识别

    让"它"认得你 --利用opencv快速入门人脸检测与人脸识别 opencv,顾名思义"开源,计算机视觉".OpenCV就是这样的一个特殊的框架,一群大牛然绕自 ...

  9. python提取发票信息发票识别_(附完整python源码)基于tensorflow、opencv的入门案例_发票识别二:字符分割...

    (附完整python源码)基于tensorflow.opencv的入门案例_发票识别二:字符分割 发布时间:2018-05-14 20:16, 浏览次数:1201 , 标签: python tenso ...

最新文章

  1. 云计算服务在小企业中的作用?
  2. ol xyz 加载天地图_OpenLayer学习之加载天地图
  3. synchronized关键字以及实例锁 类锁
  4. php 后台配置系统,使用 laravel-admin 配置后台管理系统
  5. 如何使用实时计算 Flink 搞定实时数据处理难题?
  6. 【物联网】OpenWrt编译和修改基础--预科
  7. php 数组插入键和值,php数组中键和值的关系
  8. zabbix基础之环境搭建
  9. LeCun自曝使用C语言23年之久,2年前才用Python,还曾短暂尝试Lua
  10. 对标小米!华为P50 Pro+将有望搭载液态镜头技术
  11. Lenovo ThinkPad T系列解决 VMware Workstation 打开虚拟机提示:Intel VT-x处于禁用状态问题
  12. jenkins:Failed to get hostnam执行启动脚本不执行
  13. 基于javaweb+jsp的户籍管理系统(JavaWeb JSP MySQL Servlet SSM SpringBoot Bootstrap)
  14. Java高性能序列化工具Kryo序列化
  15. linux shm open,undefined reference to \'shm_open\'解决办法-j_cle-ChinaUnix博客
  16. netbackup服务linux,命令行方式管理NETBACKUP
  17. 一年后反思我的围棋经历
  18. 万字拿下leetcode线性数据结构
  19. PVLAN 技术介绍
  20. java web调用百度地图_Java web与web gis学习笔记(二)——百度地图API调用

热门文章

  1. 蓝屏 STOP:c0000218 {Registry File Failure}
  2. 量化交易学习笔记(4):期望值
  3. 学软件开发的理由_成为软件开发人员的8个理由
  4. 农村小学计算机教育调查报告师范类,教育调查报告师范类3000字
  5. 如何做一个自动重复发消息的脚本
  6. python中paste函数的作用,python图片指定区域替换img.paste函数的使用
  7. jQuery实现聊天对话框
  8. HyperGBM的三种Early Stopping方式
  9. 巴萨17次射门超米兰3倍红黑替补门神5扑保平局
  10. 中国历史上15个民族的最后结局