C++ | 基于C++的OpenCV图像处理 Image Processing

图像处理的现实应用

人脸检测和识别

基于内容的图像检索content-based image retrieval

RGB图像的画质改善

医疗影像X-ray

文书处理Optical Character Recognition

工厂自动化Factory Automation

安保,监视系统

自动影像处理

增强现实augmented reality

- 虚拟现实

- 头戴式显示器 Head Mounted Display(HMD)

目录

图像处理的现实应用

1.BMP图像

 2.Opencv在C++中的图像处理

2.1.opencv读取图片、摄像头

2.2.opencv对图片进行灰度化,高斯模糊,边缘检测

2.3.opencv对图片的大小位置调整

2.4.opencv绘制形状和文字

2.5.opencv的透视变换

2.6.opencv的颜色检测

2.7.opencv的形状轮廓检测

疑问与解答?

总结

参考文献


环境Win10,VS2022

1.BMP图像

新建一个头文件,再新建一个c++空文件

 新建一个位图文件

BMP是英文Bitmap(位图)的简写,它是Windows操作系统中的标准图像文件格式,能够被多种Windows应用程序所支持。BMP图像文件是Windows采用的图形文件格式,在Windows环境下运行的所有图象处理软件都支持BMP图象文件格式。Windows系统内部各图像绘制操作都是以BMP为基础的。

BMP文件是小端格式的二进制文件,可以将 BMP 映像划分为四个区域:

  • 文件头 - 所有 BMP 图像都以五行文件头开头。其中包含有关像素数据的文件类型、文件大小和位置的信息。
  • 位图标头 - 也称为信息标头。这包含有关图像的宽度/高度,位深度等信息。
  • 颜色标题 - 包含有关色彩空间和位蒙版的信息
  • 像素数据。

PNG或JPEG图像是位的映射,但它们不被称为位图,因为它们是压缩的。有损压缩是一种压缩算法,可更改像素信息以减小文件大小,而不会对图像细节造成太大损害。由于BMP未压缩,因此它是无损的。因此,BMP图像的尺寸要大得多

BMP 图像中最后一行的第一个像素像素数据中的第一个字节表示。然后,扫描通过映射下一个像素与像素数据中的下一个字节,直到该行中的最后一个像素,从而向前移动。

扫描线类似于像素行。由于 BMP 遵循自下而上的扫描,因此第一行扫描行是 BMP 图像的最后一行。一旦扫描线为该行构造了像素,它就会向上移动一行,并开始从该行的像素数据中解析像素。

 

在上面的例子中,我们正在创建一个像素BMP图像,为了方便起见,我们可以说1px可以仅通过1字节成功打印。为了便于理解,我们已将 Pixel Data 分解为一个网格,但实际上,它只是一个连续的字节流4 x 4  4 x 4 .

示例

bmp.h

#include <vector>
struct BMPFileHeader {
    uint16_t file_type{ 0x4D42 };          // File type always BM which is 0x4D42
    uint32_t file_size{ 0 };               // Size of the file (in bytes)
    uint16_t reserved1{ 0 };               // Reserved, always 0
    uint16_t reserved2{ 0 };               // Reserved, always 0
    uint32_t offset_data{ 0 };             // Start position of pixel data (bytes from the beginning of the file)
    
};
struct BMPInfoHeader {
    uint32_t size{ 0 };                      // Size of this header (in bytes)
    int32_t width{ 0 };                      // width of bitmap in pixels
    int32_t height{ 0 };                     // width of bitmap in pixels
                                                  //       (if positive, bottom-up, with origin in lower left corner)
                                                      //       (if negative, top-down, with origin in upper left corner)
    uint16_t planes{ 1 };                    // No. of planes for the target device, this is always 1
    uint16_t bit_count{ 0 };                 // No. of bits per pixel
    uint32_t compression{ 0 };               // 0 or 3 - uncompressed. THIS PROGRAM CONSIDERS ONLY UNCOMPRESSED BMP images
    uint32_t size_image{ 0 };                // 0 - for uncompressed images
    int32_t x_pixels_per_meter{ 0 };
    int32_t y_pixels_per_meter{ 0 };
    uint32_t colors_used{ 0 };               // No. color indexes in the color table. Use 0 for the max number of colors allowed by bit_count
    uint32_t colors_important{ 0 };          // No. of colors used for displaying the bitmap. If 0 all colors are required
    
};
struct BMPColorHeader {
    uint32_t red_mask{ 0x00ff0000 };         // Bit mask for the red channel
    uint32_t green_mask{ 0x0000ff00 };       // Bit mask for the green channel
    uint32_t blue_mask{ 0x000000ff };        // Bit mask for the blue channel
    uint32_t alpha_mask{ 0xff000000 };       // Bit mask for the alpha channel
    uint32_t color_space_type{ 0x73524742 }; // Default "sRGB" (0x73524742)
    uint32_t unused[16]{ 0 };                // Unused data for sRGB color space
    
};
struct BMP {   
    BMPFileHeader file_header;
    BMPInfoHeader bmp_info_header;   
    BMPColorHeader bmp_color_header;
    std::vector<uint8_t> data;

           BMP(const char* fname) {
               read(fname);
       
    }
    
            void read(const char* fname) {
            	// ...
            
    }
    
            BMP(int32_t width, int32_t height, bool has_alpha = true) {
            	// ...
           
    }
    
            void write(const char* fname) {
           	// ...
           
    }
    
 private:
        // ...
        
};

bmp.cpp

#include "bmp.h"

 int main() {
	     // Read an image from disk, modify it and write it back:
		   BMP bmp("Shapes.bmp");
	     bmp.write("Shapes_copy.bmp");
	
		    // Create a BMP image in memory:
		    BMP bmp2(800, 600);
	
		   // Modify the pixel data:
		   // ....
		
		   // Save the image:
		    bmp2.write("Bitmap.bmp");
	
		   // Create a 24 bit per pixel image (BGR colors) and save it
		    BMP bmp3(209, 203, false);
	    // ...
		   bmp3.write("test_24bits.bmp");
	
}

 2.Opencv在C++中的图像处理

首先配置opencv在VS2022的环境,详情可参考环境配置 | 图文VS2022配置OpenCV_夏天|여름이다的博客-CSDN博客

2.1.opencv读取图片、摄像头

读取照片

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
	string path = "D:\\VSProjectFile\\cv\\lina.jpg";
	Mat img = imread(path);
	imshow("Image", img);
	waitKey(0); //显示图片不会一闪而过

	return 0;
}

 

 从文件读取视频

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
	string path = "Resources/test_video.mp4";
	VideoCapture cap(path); //视频捕捉对象
	Mat img;
	while (true) {

		cap.read(img);

		imshow("Image", img);
		waitKey(1);
	}
	return 0;
}

读取电脑摄像头

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
	VideoCapture cap(0);
	Mat img;

	while (true) {

		cap.read(img);

		imshow("Image", img);
		waitKey(1);
	}

	return 0;
}

没有摄像头,找不到摄像头时也会程序中断模式。

 2.2.opencv对图片进行灰度化,高斯模糊,边缘检测

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
	string path = "D:\\VSProjectFile\\cv\\lina.jpg";
	Mat img = imread(path);
	Mat imgGray, imgBlur, imgCanny, imgDil, imgErode;

	cvtColor(img, imgGray, COLOR_BGR2GRAY); //灰度化
	GaussianBlur(img, imgBlur, Size(3, 3), 3, 0); //高斯模糊
	Canny(imgBlur, imgCanny, 25, 75); //边缘检测

	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
	dilate(imgCanny, imgDil, kernel);
	erode(imgDil, imgErode, kernel);

	imshow("Image", img);
	imshow("ImageGray", imgGray);
	imshow("ImageBlur", imgBlur);
	imshow("ImageCanny", imgCanny);
	imshow("ImageDilation", imgDil);
	imshow("ImageErode", imgErode);
	waitKey(0);

	return 0;
}

2.3.opencv对图片的大小位置调整

 2.4.opencv绘制形状和文字

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
	//Blank Image
	Mat img(512, 512, CV_8UC3, Scalar(255, 255, 255));

	circle(img, Point(256, 256), 155, Scalar(0, 69, 255), FILLED);
	rectangle(img, Point(130, 226), Point(382, 286), Scalar(255, 255, 255), -1);
	line(img, Point(130, 296), Point(382, 296), Scalar(255, 255, 255), 2);

	putText(img, "Fine, It's OK !", Point(137, 262), FONT_HERSHEY_DUPLEX, 0.95, Scalar(0, 69, 255), 2);

	imshow("Image", img);
	waitKey(0);

	return 0;
}

2.5.opencv的透视变换

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

float w = 250, h = 350;
Mat matrix, imgWarp;

int main()
{
	string path = "D:\\VSProjectFile\\cv\\ai.png";
	Mat img = imread(path);

	Point2f src[4] = { {350, 250}, {360, 260}, {250, 250}, {125, 160} };
	Point2f dst[4] = { {0.0f, 0.0f}, {w, 0.0f}, {0.0f, h}, {w, h} };

	matrix = getPerspectiveTransform(src, dst);
	warpPerspective(img, imgWarp, matrix, Point(w, h));

	for (int i = 0; i < 4; i++) {
		circle(img, src[i], 10, Scalar(0, 0, 255), FILLED);
	}

	imshow("Image", img);
	imshow("ImageWarp", imgWarp);
	waitKey(0);

	return 0;
}

2.6.opencv的颜色检测

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

Mat imgHSV, mask;
int hmin = 0, smin = 110, vmin = 153;
int hmax = 19, smax = 240, vmax = 255;


int main()
{
	string path = "D:\\VSProjectFile\\cv\\hg1.jpg";
	Mat img = imread(path);
	cvtColor(img, imgHSV, COLOR_BGR2HSV);

	namedWindow("Trackbars", (640, 200));
	createTrackbar("Hue Min", "Trackbars", &hmin, 179);
	createTrackbar("Hue Max", "Trackbars", &hmax, 179);
	createTrackbar("Sat Min", "Trackbars", &smin, 255);
	createTrackbar("Sat Max", "Trackbars", &smax, 255);
	createTrackbar("Val Min", "Trackbars", &vmin, 255);
	createTrackbar("Val Max", "Trackbars", &vmax, 2555);


	while (true) {

		Scalar lower(hmin, smin, vmin);
		Scalar upper(hmax, smax, vmax);
		inRange(imgHSV, lower, upper, mask);

		imshow("Image", img);
		imshow("Image HSV", imgHSV);
		imshow("Image Mask", mask);
		waitKey(1);

	}

	return 0;
}

 2.7.opencv的形状轮廓检测


#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

void getContours(Mat imgDil, Mat img) {

	vector<vector<Point>> contours; //轮廓数据
	vector<Vec4i> hierarchy;

	findContours(imgDil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); //通过预处理的二值图像找到所有轮廓contours
	//drawContours(img, contours, -1, Scalar(255, 0, 255), 2); //绘制所有轮廓

	for (int i = 0; i < contours.size(); i++)
	{
		double area = contourArea(contours[i]); //计算每个轮廓区域
		cout << area << endl;

		vector<vector<Point>> conPoly(contours.size());
		vector<Rect> boundRect(contours.size());
		string objectType;

		if (area > 1000) //过滤噪声
		{
			//找轮廓的近似多边形或曲线
			double peri = arcLength(contours[i], true);
			approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);

			cout << conPoly[i].size() << endl;
			boundRect[i] = boundingRect(conPoly[i]); //找每个近似曲线的最小上边界矩形

			int objCor = (int)conPoly[i].size();

			if (objCor == 3) { objectType = "Tri"; }
			if (objCor == 4) {

				float aspRatio = (float)boundRect[i].width / boundRect[i].height; //宽高比
				cout << aspRatio << endl;
				if (aspRatio > 0.95 && aspRatio < 1.05) {
					objectType = "Square";
				}
				else {
					objectType = "Rect";
				}
			}
			if (objCor > 4) { objectType = "CirCle"; }

			drawContours(img, conPoly, i, Scalar(255, 0, 255), 2); //绘制滤除噪声后的所有轮廓
			rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5); //绘制边界框
			putText(img, objectType, { boundRect[i].x, boundRect[i].y - 5 }, FONT_HERSHEY_PLAIN, 1, Scalar(0, 69, 255), 1);
		}
	}
}

int main()
{
	string path = "D:\\VSProjectFile\\cv\\tx.png";
	Mat img = imread(path);
	Mat imgGray, imgBlur, imgCanny, imgDil;

	// Preprocessing
	cvtColor(img, imgGray, COLOR_BGR2GRAY);
	GaussianBlur(imgGray, imgBlur, Size(3, 3), 3, 0);
	Canny(imgBlur, imgCanny, 25, 75);
	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
	dilate(imgCanny, imgDil, kernel);

	getContours(imgDil, img);

	imshow("Image", img);
	/*imshow("Image Gray", imgGray);
	imshow("Image Blur", imgBlur);
	imshow("Image Canny", imgCanny);
	imshow("Image Dil", imgDil);*/

	waitKey(0);

	return 0;
}

疑问与解答?

1.照片的像素是怎么存储在计算机中的?

日常生活中我们的照片都是彩色的,也就是RGB三原色,

也可以表示为

这些指标中的每一个都将具有介于 0 到 255 之间的值,其中每个数字都表示像素的强度,或者您可以说红色、绿色和蓝色的阴影。最后,所有这些通道或所有这些矩阵都叠加在一起,因此当加载到计算机中时,图像的形状将是 :N*M*3

2.计算机怎么实现2次元处理?

创建一个图像对象,根据RGB的像素值,可以从存储数字图像的文件创建 Image 对象。图像对象具有与图像中的宽度、高度和像素集合相对应的属性。要求图像对象使用 and 方法返回其大小。我们还可以使用 从图像中的特定位置获取像素,并使用 更改特定位置的像素。为了处理所有像素,系统地访问图像中的所有行和列。执行此操作的最佳方法是使用嵌套迭代

总结

1.照片上的一个像素是p,那么p[x]指的就是p的动态数组(dynamic array),p[a][b]指的就是动作位置空间。

参考文献

【1】OpenCV入门【C++版】_Star_ID的博客-CSDN博客_c++ opencv教程

【2】如何将图像存储在计算机上|灰度和 RGB 图像格式 (analyticsvidhya.com)

【3】8.11. 2-Dimensional Iteration: Image Processing — How to Think like a Computer Scientist: Interactive Edition (runestone.academy) 

  • 6
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏天|여름이다

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值