指针访问图像像素

指针访问

ptr操作时通过指针偏移的方式进行像素的查找、遍历和修改的,因此效率相对较高。

      uchar pixel_value = Mat.ptr<uchar>(row)[col]; //获取某个像素值
       Mat.ptr<uchar>(row)[col] = pixel_value; //修改某个像素值,也可以直接赋值为0,1,...,255

对于彩色图:row = Mat.rows; col = Mat.cols * Mat.channels();

对于灰度图:row = Mat.rows; col = Mat.cols;

uchar * datarow = Mat.ptr<uchar>(row); //获取某行的首地址

对于彩色图像也可以表示为:

Vec3b color_value = Mat.ptr<Vec3b>(row)[col];
Mat.ptr<Vec3b>(row)[col] = color_value;

实现示例:

    Mat srcImage = Mat(7, 15, CV_8UC1,Scalar::all(0));
    cout << "srcImage = " << endl << srcImage << endl;
    namedWindow("原图",WINDOW_NORMAL);
    imshow("原图", srcImage);
 
    for (int i = 0; i < srcImage.rows; i++)
    {
        for (int j = 0; j < srcImage.cols; j++)
        {
            if (i==1 || i==5)
            {
                if ((j >= 1 && j <= 4) || (j >= 10 && j <= 13))
                {
                    srcImage.ptr<uchar>(i)[j] = 255;
                }
            }
            else if (i == 2 || i== 3 || i==4)
            {
                if (j>=1 && j<=13)
                {
                    srcImage.ptr<uchar>(i)[j] = 255;
                }
            }
        }
    }
 
    cout << "srcImage = " << endl << srcImage << endl;
    namedWindow("修改图", WINDOW_NORMAL);
    imshow("修改图", srcImage);

参考代码

//-----------------------------------【全局函数声明部分】-----------------------------------
//          描述:全局函数声明
//-----------------------------------------------------------------------------------------------
void colorReduce(Mat& inputImage, Mat& outputImage, int div);  
void ShowHelpText();
//--------------------------------------【main( )函数】---------------------------------------
//          描述:控制台应用程序的入口函数,我们的程序从这里开始执行
//-----------------------------------------------------------------------------------------------
int main( )  
{  
//【1】创建原始图并显示
Mat srcImage = imread("1.jpg");  
imshow("原始图像",srcImage);  
//【2】按原始图的参数规格来创建创建效果图
Mat dstImage;
dstImage.create(srcImage.rows,srcImage.cols,srcImage.type());//效果图的大小、类型与原图片相同 
ShowHelpText();
//【3】记录起始时间
double time0 = static_cast<double>(getTickCount());  
//【4】调用颜色空间缩减函数
colorReduce(srcImage,dstImage,32);  
//【5】计算运行时间并输出
time0 = ((double)getTickCount() - time0)/getTickFrequency();
cout<<"\t此方法运行时间为: "<<time0<<"秒"<<endl;  //输出运行时间
//【6】显示效果图
imshow("效果图",dstImage);  
waitKey(0);  
}  
//---------------------------------【colorReduce( )函数】---------------------------------
//          描述:使用【指针访问:C操作符[ ]】方法版的颜色空间缩减函数
//----------------------------------------------------------------------------------------------
void colorReduce(Mat& inputImage, Mat& outputImage, int div)  
{  
//参数准备
outputImage = inputImage.clone();  //拷贝实参到临时变量
int rowNumber = outputImage.rows;  //行数
int colNumber = outputImage.cols*outputImage.channels();  //列数 x 通道数=每一行元素的个数
//双重循环,遍历所有的像素值
for(int i = 0;i < rowNumber;i++)  //行循环
{  
uchar* data = outputImage.ptr<uchar>(i);  //获取第i行的首地址
for(int j = 0;j < colNumber;j++)   //列循环
{   
// ---------【开始处理每个像素】-------------     
data[j] = data[j]/div*div + div/2;  
// ----------【处理结束】---------------------
}  //行处理结束
}  
}  
//-----------------------------------【ShowHelpText( )函数】----------------------------------
//          描述:输出一些帮助信息
//----------------------------------------------------------------------------------------------
void ShowHelpText()
{
//输出欢迎信息和OpenCV版本
printf("\n\n\t\t\t非常感谢购买《OpenCV3编程入门》一书!\n");
printf("\n\n\t\t\t此为本书OpenCV2版的第21个配套示例程序\n");
printf("\n\n\t\t\t   当前使用的OpenCV版本为:" CV_VERSION );
printf("\n\n  ----------------------------------------------------------------------------\n");
}

整个程序的主要部分在这里:
//双重循环,遍历所有的像素值
for(int i = 0;i < rowNumber;i++)  //行循环
{  
uchar* data = outputImage.ptr<uchar>(i);  //获取第i行的首地址
for(int j = 0;j < colNumber;j++)   //列循环
{   
// ---------【开始处理每个像素】-------------     
data[j] = data[j]/div*div + div/2;  
// ----------【处理结束】---------------------
}  //行处理结束
}  

主要思想是:双重循环遍历所有的像素值。

行循环是rows,列循环是cols。

首先,进入行循环的时候,找到每一行的行首地址:uchar* data = outputImage.ptr<uchar>(i)。存进data指针里面。注意指针类型是uchar类型。

然后,进入列循环的时候,data[]数组的元素就是每一列的图像像素。直接使用数组的方式来访问元素即可。

就这两步。



下面看一下这个头指针ptr<uchar>():

函数原型是:

template<typename _Tp> inline _Tp*  Mat::ptr(int y)
{
    CV_DbgAssert( y == 0 || (data && dims >= 1 && (unsigned)y < (unsigned)size.p[0]) );
    return (_Tp*)(data + step.p[0]*y);
}

返回的是_Tp类型的指针。指针指向的是data数据的行首地址。step.p[0]是第一行的首地址,step是步长,最后乘以y是指向y行的首地址。

指针访问图像像素值耗时测评

// Read_Modify_Piexl_Value.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
 
#include "pch.h"
#include <iostream>
#include <opencv2/opencv.hpp>
 
using namespace std;
using namespace cv;
 
Mat readPixelNormal(Mat img, Mat thres)
{
  for (int i = 0; i < thres.rows; i++)
  {
    for (int j = 0; j < thres.cols; j++)
    {
      if (thres.at<uchar>(i, j) == 255)
          img.at<Vec3b>(i, j) = Vec3b(197, 247, 254); //BGR彩色图像素值改变
    }
  }
  return img;
}
 
Mat readPixelFast(Mat img, Mat thres)
{
  for (int i = 0; i < thres.rows; i++)
  {
    uchar *ptr = thres.ptr<uchar>(i);
    uchar *ptrColor = img.ptr<uchar>(i);
    for (int j = 0; j < thres.cols; j++)
    {
      if (ptr[j] == 255)
      {
        ptrColor[j * 3] = 197;
        ptrColor[j * 3 + 1] = 247;
        ptrColor[j * 3 + 2] = 254;
      }
    }
  }
  return img;
}
 
int main()
{
  Mat img = imread("S.png");
  Mat gray, thres;
  cvtColor(img, gray, COLOR_BGR2GRAY);
  threshold(gray, thres, 0, 255, THRESH_BINARY | THRESH_OTSU);
  double start = static_cast<double>(getTickCount());
  //Mat result = readPixelNormal(img, thres);
  Mat result = readPixelFast(img, thres);
  double end = static_cast<double>(getTickCount());
  double useTime = ((double)end - start) / getTickFrequency() * 1000;
  cout << "use-time = " << useTime << "ms" << endl;
  imwrite("res.png", result);
  return 0;
}

  1. 传统方法 480ms[3M左右图片]
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main()
{
    Mat m_img = imread("D://vvoo/lena.jpg");
    Mat src(m_img.rows, m_img.cols, CV_8UC1, Scalar(0));
    cvtColor(m_img, src, CV_RGB2GRAY);
 
    Mat dstImage(src.rows, src.cols, CV_8UC1, Scalar(0));
    for (int i = 1; i < src.rows - 1; i++)
    {
        for (int j = 1; j < src.cols - 1; j++)
        {
            dstImage.data[i*dstImage.step + j] = sqrt((src.data[(i - 1)*src.step + j + 1]
                + 2 * src.data[i*src.step + j + 1]
                + src.data[(i + 1)*src.step + j + 1]
                - src.data[(i - 1)*src.step + j - 1] - 2 * src.data[i*src.step + j - 1]
                - src.data[(i + 1)*src.step + j - 1])*(src.data[(i - 1)*src.step + j + 1]
                + 2 * src.data[i*src.step + j + 1] + src.data[(i + 1)*src.step + j + 1]
                - src.data[(i - 1)*src.step + j - 1] - 2 * src.data[i*src.step + j - 1]
                - src.data[(i + 1)*src.step + j - 1]) + (src.data[(i - 1)*src.step + j - 1] + 2 * src.data[(i - 1)*src.step + j]
                + src.data[(i - 1)*src.step + j + 1] - src.data[(i + 1)*src.step + j - 1]
                - 2 * src.data[(i + 1)*src.step + j]
                - src.data[(i + 1)*src.step + j + 1])* (src.data[(i - 1)*src.step + j - 1] + 2 * src.data[(i - 1)*src.step + j]
                + src.data[(i - 1)*src.step + j + 1] - src.data[(i + 1)*src.step + j - 1]
                - 2 * src.data[(i + 1)*src.step + j]
                - src.data[(i + 1)*src.step + j + 1]));
 
        }
 
    }
    Mat grad_y(src.rows, src.cols, CV_8UC1, Scalar(0));
    {
        for (int i = 1; i < src.rows - 1; i++)
        {
            for (int j = 1; j < src.cols - 1; j++)
            {
                grad_y.data[i*grad_y.step + j] = abs((src.data[(i - 1)*src.step + j + 1]
                    + 2 * src.data[i*src.step + j + 1]
                    + src.data[(i + 1)*src.step + j + 1]
                    - src.data[(i - 1)*src.step + j - 1] - 2 * src.data[i*src.step + j - 1]
                    - src.data[(i + 1)*src.step + j - 1]));
            }
        }
    }
    Mat grad_x(src.rows, src.cols, CV_8UC1, Scalar(0));
    {
        for (int i = 1; i < src.rows - 1; i++)
        {
            for (int j = 1; j < src.cols - 1; j++)
            {
                grad_x.data[i*grad_x.step + j] = sqrt((src.data[(i - 1)*src.step + j - 1] + 2 * src.data[(i - 1)*src.step + j]
                    + src.data[(i - 1)*src.step + j + 1] - src.data[(i + 1)*src.step + j - 1]
                    - 2 * src.data[(i + 1)*src.step + j]
                    - src.data[(i + 1)*src.step + j + 1])* (src.data[(i - 1)*src.step + j - 1] + 2 * src.data[(i - 1)*src.step + j]
                    + src.data[(i - 1)*src.step + j + 1] - src.data[(i + 1)*src.step + j - 1]
                    - 2 * src.data[(i + 1)*src.step + j]
                    - src.data[(i + 1)*src.step + j + 1]));
            }
        }
    }
    imshow("原图", src);
    imshow("gradient", dstImage);
    imshow("Vertical gradient", grad_y);
    imshow("Horizontal gradient", grad_x);
 
    waitKey(0);
    return 0;
}
  1. 指针访问 耗时20ms[3M左右图片]
#include<iostream>
#include<opencv2\opencv.hpp>
using namespace std;
using namespace cv;

int main()
{
    Mat src = imread("facetest\\face0.jpg",0);
    //GaussianBlur(src, src, Size(5,5), 0);
    medianBlur(src,src,3);
    Mat gx(src.size(),src.type(),Scalar(0));
    Mat gy(src.size(), src.type(), Scalar(0));
    Mat scharr_y(src.size(),src.type(),Scalar(0));


    int Gx[9] = {-1,0,1,-2,0,2,-1,0,1};//gx方向的滤波模板
    int Gy[9] = {1,2,1,0,0,0,-1,-2,-1};//gy方向的滤波模板
    int scharrx [9]= {-3,0,3,-10,0,10,-3,0,3};
    int scharry[9] = {-3,-10,-3,0,0,0,3,10,3};
    if (!src.isContinuous())
    {
        cout << "不连续内存!!" << endl;
        return 0;
    }
    //是连续内存用指针访问

    uchar *px = gx.data;
    uchar *py=gy.data;
    uchar *s = src.data;
    uchar *pscharr_y = scharr_y.data;
    int sstep = src.step[0];
    int xstep = gx.step[0];
    int ystep = gy.step[0];
    int scstep = scharr_y.step[0];
    for (int i = 1; i <src. rows-1; i++)
    {

        for(int j=1;j<src.cols-1;j++)
        {
            py[i*ystep + j] = abs(Gy[0] * s[(i - 1)*sstep + j - 1] 
                + Gy[1] * s[(i - 1)*sstep + j]
                +Gy[2]*s[(i-1)*sstep+j+1]
                +Gy[3]*s[i*sstep+j-1]
                +Gy[4]*s[i*sstep+j]
                +Gy[5]*s[i*sstep+j+1]
                +Gy[6]*s[(i+1)*sstep+j-1]
                +Gy[7] * s[(i + 1)*sstep + j ]
                +Gy[8] * s[(i + 1)*sstep + j + 1]);

            pscharr_y[i*scstep + j] = abs(scharry[0]*s[(i-1)*sstep+j-1]
                + scharry[1] * s[(i - 1)*sstep + j]
                + scharry[2] * s[(i - 1)*sstep + j + 1]
                + scharry[3] * s[i*sstep + j - 1]
                + scharry[4] * s[i*sstep + j]
                + scharry[5] * s[i*sstep + j + 1]
                + scharry[6] * s[(i + 1)*sstep + j - 1]
                + scharry[7] * s[(i + 1)*sstep + j]
                + scharry[8] * s[(i + 1)*sstep + j + 1]);

        }

    }
    medianBlur(gy,gy,3);
    medianBlur(scharr_y,scharr_y,3);
    imshow("gy",gy);
    imshow("scharr_y", scharr_y);

    imshow("src",src);
    waitKey(0);
}

参考文献

OpenCV、EmguCV和OpenCvSharp指针访问图像像素值耗时测评(附源码)

https://blog.csdn.net/Liuzhu_shusheng_DH/article/details/118558406?spm=1001.2014.3001.5502

https://blog.csdn.net/zhulinzhulinlin/article/details/78563000?ops_request_misc=&request_id=&biz_id=102&utm_term=%E6%8C%87%E9%92%88%E5%AE%9E%E7%8E%B0Sobel&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-3-78563000.142^v73^insert_down3,201^v4^add_ask,239^v1^insert_chatgpt&spm=1018.2226.3001.4187

访问图像中像素的三种方法:1. 指针访问

访问图像中像素的三种方法:2. 迭代器访问

访问图像中像素的三种方法:3. 动态地址计算配合at方法

猜你喜欢

转载自blog.csdn.net/Yangy_Jiaojiao/article/details/129053942
今日推荐