第3讲: 快速过一下图像基本操作(二)

目录

一、前言

二、代码片段

1、获取图像基本属性

2、计算灰度特征

3、均值滤波

4、中值滤波

5、高斯滤波

6、双边滤波

7、遍历像素 方式一 行遍历

8、遍历像素 方式二 直接遍历

9、遍历像素 方式三 指针

10、遍历图像,指针 (适用于灰度图、彩色图等)

11、遍历图像,指针

12、图像平移

13、图像旋转

14、调整图像方向

15、图像融合

三、完整代码

四、总结


一、前言

        快速过一下图像基本操作,不做过多原理解释。代码都有,具体细节可自行琢磨,举一反三。

二、代码片段

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;
}

四、总结

        本文快速过了一下图像的基本操作,常用滤波方式、像素遍历方式,仿射变换及加权融合等操作,为本系列后续较难知识的深入学习奠定基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值