系列文章目录

C++ OpenCV4.5环境搭建(一)
C++ OpenCV4.5常用API查询手册(二)


前言

该篇主要讲解OpenCV对图像的部分处理



一、加载/修改/保存图片

添加一个新建源文件main.cpp,代码如下(示例):

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

using namespace std;
using namespace cv;

int main(void)
{
	// 加载图像文件成为一个Mat对象
	Mat img = imread("E:\\1.jpg");
	if (img.empty())
	{
		cout << "can not load image.." << endl;
		return -1;
	}
	// 创建一个OpenCV窗口
	namedWindow("测试");
	// 根据窗口名称显示图像到指定窗口
	imshow("测试", img);
	
	Mat outImg;
	// 将一个图像转换成灰度图像
	cvtColor(img, outImg,COLOR_BGR2GRAY);
	namedWindow("测试输出");
	imshow("测试输出", outImg);
	
	// 保存图像文件到指定路径,只有8位和16位的PNG、JPG和Tiff文件格式,并且是单通道或三通道的BGR的图像才可通过改方式保存;保存PNG格式的时候可以保存透明通道图片;可以指定压缩参数
	imwrite("D:\\out.jpg",outImg);
	
	// waitKey是给它一个指令才关闭,waitKey(0)等待键盘按下时关闭,任意键
	waitKey(0);
	
	return 0;
}

二、掩膜操作

1、掩膜操作原理

矩阵的掩膜操作很简单,就是先读取每个像素点的像素值,根据计算公式来重新计算该像素点的像素值,计算公式如下:

I(i, j) = 5*(i, j) - [I(i-1,j) + I(i+1,j) + I(i,j-1) + I(i,j+1)]

如下图所示,红色是中心像素,中心像素×5,减去上下左右四个点的像素,最终结果复制给中心像素;
从上到下、从左到右,对每个像素做相同处理,重新计算每个像素的像素值,最终结果就是对比度提高后的输出图像Mat对象;

| 0 -1 0 |

| 1 5 -1 |

| 0 -1 0 |

在这里插入图片描述

1、读取像素值

读取一个单通道GRAY像素点的像素值(CV_8UC1)

int intensity = img.at<uchar>(x, y); 	 
//或 
int intensity = img.at<uchar>(Point(x, y));

读取一个三通道RGB像素点的像素值

// Vec3b对应的三通道顺序是blue、green、read的uchar类型数据 
Vec3b intensity = img.at<Vec3b>(x, y); 
int blue = intensity.val[0]; 
int green = intensity.val[1];  
int red = intensity.val[2];

2、修改像素值

修改一个单通道GRAY图像的像素值

img.at<uchar>(x, y) = 128;

修改一个三通道RGB图像的像素值

img.at<Vec3b>(x, y)[0] = 128; // blue   
img.at<Vec3b>(x, y)[1] = 128; // green 
img.at<Vec3b>(x, y)[2] = 128; // red

空白图像赋值

 img = Scalar(0);

示例代码如下:

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

using namespace std;
using namespace cv;

int main(void)
{
	Mat srcImg, dstImg;
	srcImg = imread("D:\\1.jpg");
	if (!srcImg.data)
	{
		cout << "can not load image.." << endl;
		return -1;
	}
	namedWindow("测试");
	imshow("测试", srcImg);

	dstImg.create(srcImg.size(), srcImg.type());
	int iHeight = srcImg.rows;
	int iWidth = srcImg.cols;
	int iChannels = srcImg.channels();

	for (int row = 0; row < iHeight; ++row)
	{
		for (int col = 0; col < iWidth; ++col)
		{
			if (iChannels == 1)
			{
				int iGray = srcImg.at<uchar>(row, col);
				// 翻转图像颜色(源像素 = 255 - 源像素)
				dstImg.at<uchar>(row, col) = 255 - iGray;
			}
			else
			{
				int iB = srcImg.at<Vec3b>(row, col)[0];
				int iG = srcImg.at<Vec3b>(row, col)[1];
				int iR = srcImg.at<Vec3b>(row, col)[2];
				// 翻转图像颜色(源像素 = 255 - 源像素)
				dstImg.at<Vec3b>(row, col)[0] = 255 - iB;
				dstImg.at<Vec3b>(row, col)[1] = 255 - iG;
				dstImg.at<Vec3b>(row, col)[2] = 255 - iR;
			}
		}
	}

	// 翻转图像颜色,效果与(源像素 = 255 - 源像素)一样
	//bitwise_not(srcImg, dstImg);

	namedWindow("测试输出");
	imshow("测试输出", dstImg);
	waitKey(0);

	return 0;
}

3、掩膜操作实现

代码如下(示例):

#include <opencv2/opencv.hpp>
#include <Windows.h>

using namespace std;
using namespace cv;

int main(void)
{
	Mat srcImg, dstImg;
	srcImg = imread("D:\\1.png");
	if (!srcImg.data)
	{
		cout << "can not load image.." << endl;
		return -1;
	}
	namedWindow("测试");
	imshow("测试", srcImg);

	/************************方法一*************************/
	// 像素列数,等于图像列数*通道数,因为第一列不计算,所以列数-1
	int iCols = (srcImg.cols - 1) * srcImg.channels();
	// 像素行数
	int iRows = srcImg.rows;
	// 像素列数起始偏移长度
	int iOffsetY = srcImg.channels();
	// 根据源图像的大小和图片类型,创建一个空的Mat对象
	dstImg = Mat::zeros(srcImg.size(), srcImg.type());
	for (int row = 1; row < iRows - 1; ++row)
	{
		// Mat.ptr<uchar>(int i):获取像素矩阵的指针,索引i表示第几行,从0开始计数
		// 获取源图像前一行的像素矩阵指针
		const uchar* pPrevious = srcImg.ptr<uchar>(row - 1);
		// 获取源图像当前行的像素矩阵指针
		const uchar* pCurrent = srcImg.ptr<uchar>(row);
		// 获取源图像下一行的像素矩阵指针
		const uchar* pNext = srcImg.ptr<uchar>(row + 1);
		// 获取目标图像当前行像素矩阵指针
		uchar* pOutput = dstImg.ptr<uchar>(row);
		for (int col = iOffsetY; col < iCols; ++col)
		{
			// 掩膜计算公式:I(i, j) = 5*(i, j) - [I(i-1,j) + I(i+1,j) + I(i,j-1) + I(i,j+1)]
			// saturate_cast<uchar>:像素范围处理,确保RGB值的范围在0-255之间,小于0返回0,大于255返回255,其他返回实际的值
			pOutput[col] = saturate_cast<uchar>(5 * pCurrent[col] -
				(pCurrent[col - iOffsetY] + pCurrent[col + iOffsetY] + pPrevious[col] + pNext[col]));
		}
	}

	/************************方法二*************************/
	/*
	// 定义一个掩膜
	Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
	
	// filter2D:图像卷积运算函数
	// 参数一:源图像,Mat类型对象
	// 参数二:目标图像,Mat类型对象
	// 参数三:位图深度,有32、24、8等
	// 参数四:掩膜,Mat类型对象
	filter2D(srcImg, dstImg, srcImg.depth(), kernel);
	*/

	namedWindow("测试输出");
	imshow("测试输出", dstImg);
	waitKey(0);
	
	return 0;
}

三、图像混合

1、理论-线性混合操作

计算公式如下图

在这里插入图片描述

2、相关API(addWeighted)

// 功能:图像混合,两张图像的大小和类型必须一致
// 参数:src1 输入图像1,Mat对象类型
//		alpha 输入图像1的混合权重
//		src2 输入图像2,Mat对象类型
// 		beta 输入图像2的混合权重
//		gamma 图像1与图像2的像素值、通过上面的公式相加后,会加上该数值
//		dst 输出混合图像,Mat对象类型
//		dtype ...默认-1
// 返回:无
void addWeighted(InputArray src1, double alpha, InputArray src2,
                              double beta, double gamma, OutputArray dst, int dtype = -1);

代码如下(示例):

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

using namespace std;
using namespace cv;

int main(void)
{
	Mat srcImg, srcImg2, dstImg;
	srcImg = imread("D:\\1.jpg");
	if (!srcImg.data)
	{
		cout << "can not load image1.." << endl;
		return -1;
	}

	srcImg2 = imread("D:\\2.jpg");
	if (!srcImg2.data)
	{
		cout << "can not load image2.." << endl;
		return -1;
	}

	double dAlpha = 0.5;
	if (srcImg.rows == srcImg2.rows && srcImg.cols == srcImg2.cols && srcImg.type() == srcImg2.type())
	{
		addWeighted(srcImg, dAlpha, srcImg2, 1 - dAlpha, 0, dstImg);
		
		imshow("测试", srcImg);
		imshow("测试2", srcImg2);
		imshow("测试输出", dstImg);
	}

	waitKey(0);

	return 0;
}

四、调整图像亮度与对比度

调整图像亮度和对比度属于像素变换-点操作,公式如下

g(i, j) = αƒ(i, j) + β,其中α > 0,β是增益变量

示例代码如下:

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

using namespace std;
using namespace cv;

int main()
{
    Mat srcImg, dstImg;
    srcImg = imread("/home/lzy/1.jpeg");
    if(!srcImg.data)
    {
        cout << "load image failed.." << endl;
        return -1;
    }

    Mat srcImgGray;
    cvtColor(srcImg, srcImgGray, COLOR_BGR2GRAY);

    imshow("测试", srcImgGray);

    int iRows = srcImg.rows;
    int iCols = srcImg.cols;
    dstImg = Mat::zeros(srcImgGray.size(), srcImgGray.type());
    float fAlpha = 1.8;
    float fBeta = 30;
    for(int i=0; i<iRows; i++)
    {
        for(int j=0; j<iCols; j++)
        {
            if(srcImgGray.channels() == 1)
            {
                float f = srcImgGray.at<uchar>(i, j);
                dstImg.at<uchar>(i, j) = saturate_cast<uchar>(f * fAlpha + fBeta);
            }
            else if(srcImg.channels() == 3)
            {
                float fGreen = srcImg.at<Vec3b>(i, j)[0];
                float fBlue = srcImg.at<Vec3b>(i, j)[1];
                float fRed = srcImg.at<Vec3b>(i, j)[2];

                dstImg.at<Vec3b>(i, j)[0] = saturate_cast<uchar>(fGreen * fAlpha + fBeta);
                dstImg.at<Vec3b>(i, j)[1] = saturate_cast<uchar>(fBlue * fAlpha + fBeta);
                dstImg.at<Vec3b>(i, j)[2] = saturate_cast<uchar>(fRed * fAlpha + fBeta);
            }
        }
    }

    imshow("测试输出", dstImg);

    waitKey(0);

    return 0;
}

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐