目录
一、前言
快速过一下图像基本操作,不做过多原理解释。代码都有,具体细节可自行琢磨,举一反三。
二、代码片段
1、获取图像基本属性
图像的常用的基本属性,包括图像通道数,行数,列数等。
int channels = src.channels();
int rows = src.rows;
int cols = src.cols;
2、计算灰度特征
常用的灰度特征有灰度均值,灰度最大值、灰度最小值,灰度标准差等。
int min, max;
cv::Point minLoc, maxLoc;
minMaxLoc(src, &min, &max, &minLoc, &maxLoc);
cv::Scalar scalar = cv::mean(src);
std::cout << "最小灰度值:" << min << ", 位于:" << minLoc << std::endl;
std::cout << "最大灰度值:" << max << ",位于:" << maxLoc << std::endl;
std::cout << "平均灰度值:" << scalar << std::endl;
cv:: Mat meanMat, stdDevMat;
meanStdDev(src, meanMat, stdDevMat);
std::cout << "平均灰度值:" << meanMat << std::endl;
std::cout << "标准差:" << stdDevMat << std::endl;
3、均值滤波
在卷积核范围内,求所有像素的平均值,作为当前像素点滤波后的值
cv::Mat dst_mean;
cv::blur(src, dst_mean,cv::Size(5,5));
cv::imshow("dst_mean", dst_mean);
4、中值滤波
在卷积核范围内,求所有像素的中间值,作为当前像素点滤波后的值
cv::Mat dst_median;
cv::medianBlur(src, dst_median,5);
cv::imshow("dst_median", dst_median);
5、高斯滤波
在卷积核范围内,根据高斯概率函数当前像素点滤波后的值
cv::Mat dst_gauss;
cv::GaussianBlur(src, dst_gauss, cv::Size(5, 5), 1);
cv::imshow("dst_gauss", dst_gauss);
6、双边滤波
bilateralFilter 参数:
src 源8位或浮点,1通道或3通道图像。
dst 与src具有相同大小和类型的目标图像。
d 过滤期间使用的每个像素邻域的直径。如果它是非正数,则从sigmaSpace计算。
sigmaColor 过滤颜色空间中的西格玛。参数的值越大意味着像素邻域内的更远的颜色(参见sigmaSpace)将混合在一起,从而产生更大的半等颜色区域。
sigmaSpace 在坐标空间中过滤西格玛。较大的参数值意味着只要它们的颜色足够接近,更远的像素就会相互影响(参见sigmaColor)。当d > 0时,无论sigmaSpace如何,它都指定邻域大小。否则,d与sigmaSpace成比例。
borderType 用于外推图像外部像素的边框模式,请参阅BorderTypes
cv::Mat dst_bila;
cv::bilateralFilter(src, dst_bila, 0, 1, 1, cv::BORDER_DEFAULT);
cv::imshow("dst_bila", dst_bila);
enum BorderTypes {
BORDER_CONSTANT = 0,
BORDER_REPLICATE = 1,
BORDER_REFLECT = 2,
BORDER_WRAP = 3,
BORDER_REFLECT_101 = 4,
BORDER_TRANSPARENT = 5,
BORDER_REFLECT101 = BORDER_REFLECT_101,
BORDER_DEFAULT = BORDER_REFLECT_101,
BORDER_ISOLATED = 16
};
7、遍历像素 方式一 行遍历
if (channels == 1){
for (int y = 0; y < src.rows; y++){
uchar* ptr = src.ptr<uchar>(y);
for (int x = 0; x < src.cols; x++){
ptr[x] = 255 - ptr[x]; // 像素取反
}
}
}
else if (channels == 3){
for (int y = 0; y < src.rows; y++){
cv::Vec3b * ptr = src.ptr<cv::Vec3b>(y);
for (int x = 0; x < src.cols; x++){
cv::Vec3b bgr = ptr[x];
bgr[0] = 255 - bgr[0];
bgr[1] = 255 - bgr[1];
bgr[2] = 255 - bgr[2];
ptr[x] = bgr;
}
}
}
8、遍历像素 方式二 直接遍历
if (channels == 1){
for (int i = 0; i < src.rows; i++){
for (int j = 0; j < src.cols; j++){
src.at<uchar>(i, j) = 255 - src.at<uchar>(i, j); // 像素取反
}
}
}
else if (channels == 3){
for (int y = 0; y < src.rows; y++){
for (int x = 0; x < src.cols; x++){
cv::Vec3b bgr = src.at<cv::Vec3b>(y, x);
bgr[0] = 255 - bgr[0];
bgr[1] = 255 - bgr[1];
bgr[2] = 255 - bgr[2];
src.at<cv::Vec3b>(y, x) = bgr;
}
}
}
9、遍历像素 方式三 指针
if (channels == 1)
{
for (int y = 0; y < src.rows; y++) {
uchar* pix = src.data + y * src.step;
for (int x = 0; x < src.cols; x++) {
*pix = 255 - *pix;
}
}
}
if (channels == 3){
for (int y = 0; y < src.rows; y++){
uchar* pix = src.data + y * src.step;
for (int x = 0; x < src.cols; x++){
pix[0] = 255 - pix[0];
pix[1] = 255 - pix[1];
pix[2] = 255 - pix[2];
}
}
}
10、遍历图像,指针 (适用于灰度图、彩色图等)
int r, c;
r = src.rows;
c = src.cols * src.channels();
cv::Mat dst = src.clone();
for (int k = 0; k < r; k++) { //行
uchar* data_in = src.ptr<uchar>(k);//行指针
for (int i = 0; i < c; i++) { //列
data_in[i] = 255 - data_in[i]; //像素取反
}
}
11、遍历图像,指针
此方法需要判断图像在内存中存储的空间是否连续,如果连续则可以使用。
//11、遍历图像,指针 (适用于灰度图、彩色图等),需要判断图像在内存中的存储是否连续
if (src.isContinuous()){ // 判断图片在内存中是否连续存储
for (int i = 0; i < src.rows; i++){
uchar* data_in = src.ptr<uchar>(i);
for (int j = 0; j < src.cols; j++){
*data_in++ = 255 - (*data_in++); // 像素取反
}
}
}
12、图像平移
基本的仿射变换
cv::Mat dst_trans;
float warp_values[] = { 1.0, 0.0, 100, 0.0, 1.0, 100 };
cv::Mat trans_matrix = cv::Mat(2, 3, CV_32F, warp_values);
cv::warpAffine(src, dst_trans, trans_matrix, src.size());
13、图像旋转
基本的仿射变换
cv::Mat dst_rotate;
double angle = 45;
cv::Point2f center((src.cols - 1) / 2.0, (src.rows - 1) / 2.0);
cv::Mat rot_matix = getRotationMatrix2D(center, angle, 1.0);
warpAffine(src, dst_rotate, rot_matix, src.size());
14、调整图像方向
翻转图像方向,常用于UI界面显示图像时,修改图片显示方向
cv::Mat dst_flip;
//cv::flip(src, dst_flip, 0); // 上下反转
cv::flip(src, dst_flip, 1); // 左右反转
15、图像融合
两张图像加权
cv::Mat sence = cv::imread("sence.jpg", cv::IMREAD_GRAYSCALE);
cv::Mat fusion = cv::Mat::zeros(src.size(),CV_8UC1);
cv::resize(sence, sence, src.size());
cv::addWeighted(src, 0.5,sence, 0.4,1, fusion);
cv::imshow("fusion", fusion);
cv::waitKey(0);
三、完整代码
#include <opencv2/opencv.hpp>
int main()
{
cv::Mat src = cv::imread("people.jpg", cv::IMREAD_GRAYSCALE);
// 1 获取图像基本属性
int channels = src.channels();
int rows = src.rows;
int cols = src.cols;
// 2 计算灰度图最大/最小灰度值、平均灰度、表准差等特征
double min, max;
cv::Point minLoc, maxLoc;
minMaxLoc(src, &min, &max, &minLoc, &maxLoc);
cv::Scalar scalar = cv::mean(src);
std::cout << "最小灰度值:" << min << ", 位于:" << minLoc << std::endl;
std::cout << "最大灰度值:" << max << ",位于:" << maxLoc << std::endl;
std::cout << "平均灰度值:" << scalar << std::endl;
cv:: Mat meanMat, stdDevMat;
meanStdDev(src, meanMat, stdDevMat);
std::cout << "平均灰度值:" << meanMat << std::endl;
std::cout << "标准差:" << stdDevMat << std::endl;
// 3 均值滤波
cv::Mat dst_mean;
cv::blur(src, dst_mean,cv::Size(5,5));
cv::imshow("dst_mean", dst_mean);
// 4、中值滤波
cv::Mat dst_median;
cv::medianBlur(src, dst_median,5);
cv::imshow("dst_median", dst_median);
// 5、高斯滤波
cv::Mat dst_gauss;
cv::GaussianBlur(src, dst_gauss, cv::Size(5, 5), 1);
cv::imshow("dst_gauss", dst_gauss);
// 6、双边滤波
//bilateralFilter 参数:
//src 源8位或浮点,1通道或3通道图像。
//dst 与src具有相同大小和类型的目标图像。
//d 过滤期间使用的每个像素邻域的直径。如果它是非正数,则从sigmaSpace计算。
//sigmaColor 过滤颜色空间中的西格玛。参数的值越大意味着像素邻域内的更远的颜色(参见sigmaSpace)将混合在一起,从而产生更大的半等颜色区域。
//sigmaSpace 在坐标空间中过滤西格玛。较大的参数值意味着只要它们的颜色足够接近,更远的像素就会相互影响(参见sigmaColor)。当d > 0时,无论sigmaSpace如何,它都指定邻域大小。否则,d与sigmaSpace成比例。
//borderType 用于外推图像外部像素的边框模式,请参阅BorderTypes
//
//enum BorderTypes {
// BORDER_CONSTANT = 0,
// BORDER_REPLICATE = 1,
// BORDER_REFLECT = 2,
// BORDER_WRAP = 3,
// BORDER_REFLECT_101 = 4,
// BORDER_TRANSPARENT = 5,
// BORDER_REFLECT101 = BORDER_REFLECT_101,
// BORDER_DEFAULT = BORDER_REFLECT_101,
// BORDER_ISOLATED = 16
//};
cv::Mat dst_bila;
cv::bilateralFilter(src, dst_bila, 0, 1, 1, cv::BORDER_DEFAULT);
cv::imshow("dst_bila", dst_bila);
//7、遍历像素 方式一 行遍历
if (channels == 1)
{
for (int y = 0; y < src.rows; y++)
{
uchar* ptr = src.ptr<uchar>(y);
for (int x = 0; x < src.cols; x++)
{
ptr[x] = 255 - ptr[x]; // 像素取反
}
}
}
else if (channels == 3){
for (int y = 0; y < src.rows; y++){
cv::Vec3b * ptr = src.ptr<cv::Vec3b>(y);
for (int x = 0; x < src.cols; x++){
cv::Vec3b bgr = ptr[x];
bgr[0] = 255 - bgr[0];
bgr[1] = 255 - bgr[1];
bgr[2] = 255 - bgr[2];
ptr[x] = bgr;
}
}
}
//8、遍历像素 方式二 直接遍历
if (channels == 1){
for (int i = 0; i < src.rows; i++){
for (int j = 0; j < src.cols; j++){
src.at<uchar>(i, j) = 255 - src.at<uchar>(i, j); // 像素取反
}
}
}
else if (channels == 3){
for (int y = 0; y < src.rows; y++){
for (int x = 0; x < src.cols; x++){
cv::Vec3b bgr = src.at<cv::Vec3b>(y, x);
bgr[0] = 255 - bgr[0];
bgr[1] = 255 - bgr[1];
bgr[2] = 255 - bgr[2];
src.at<cv::Vec3b>(y, x) = bgr;
}
}
}
//9、遍历像素 方式三 指针
if (channels == 1)
{
for (int y = 0; y < src.rows; y++) {
uchar* pix = src.data + y * src.step;
for (int x = 0; x < src.cols; x++) {
*pix = 255 - *pix;
}
}
}
if (channels == 3){
for (int y = 0; y < src.rows; y++){
uchar* pix = src.data + y * src.step;
for (int x = 0; x < src.cols; x++){
pix[0] = 255 - pix[0];
pix[1] = 255 - pix[1];
pix[2] = 255 - pix[2];
}
}
}
//10、遍历图像,指针 (适用于灰度图、彩色图等)
int r, c;
r = src.rows;
c = src.cols * src.channels();
cv::Mat dst = src.clone();
for (int k = 0; k < r; k++) { //行
uchar* data_in = src.ptr<uchar>(k);//行指针
for (int i = 0; i < c; i++) { //列
data_in[i] = 255 - data_in[i]; //像素取反
}
}
//11、遍历图像,指针 (适用于灰度图、彩色图等),需要判断图像在内存中的存储是否连续
if (src.isContinuous()){ // 判断图片在内存中是否连续存储
for (int i = 0; i < src.rows; i++){
uchar* data_in = src.ptr<uchar>(i);
for (int j = 0; j < src.cols; j++){
*data_in++ = 255 - (*data_in++); // 像素取反
}
}
}
//10、图像平移
cv::Mat dst_trans;
float warp_values[] = { 1.0, 0.0, 100, 0.0, 1.0, 100 };
cv::Mat trans_matrix = cv::Mat(2, 3, CV_32F, warp_values);
cv::warpAffine(src, dst_trans, trans_matrix, src.size());
//11、图像旋转
cv::Mat dst_rotate;
double angle = 45;
cv::Point2f center((src.cols - 1) / 2.0, (src.rows - 1) / 2.0);
cv::Mat rot_matix = getRotationMatrix2D(center, angle, 1.0);
warpAffine(src, dst_rotate, rot_matix, src.size());
//12、调整图像方向
cv::Mat dst_flip;
//cv::flip(src, dst_flip, 0); // 上下反转
cv::flip(src, dst_flip, 1); // 左右反转
//cv::imshow("flip", dst_flip);
//cv::waitKey(0);
//13、图像融合
cv::Mat sence = cv::imread("sence.jpg", cv::IMREAD_GRAYSCALE);
cv::Mat fusion = cv::Mat::zeros(src.size(),CV_8UC1);
cv::imshow("fusion", fusion);
cv::waitKey(0);
//cv::Mat fusion = src.clone();
cv::resize(sence, sence, src.size());
cv::addWeighted(src, 0.5,sence, 0.4,1, fusion);
return 0;
}
四、总结
本文快速过了一下图像的基本操作,常用滤波方式、像素遍历方式,仿射变换及加权融合等操作,为本系列后续较难知识的深入学习奠定基础。