zoukankan      html  css  js  c++  java
  • opencv学习笔记(九)Mat 访问图像像素的值

    对图像的像素进行访问,可以实现空间增强,反色,大部分图像特效系列都是基于像素操作的。图像容器Mat是一个矩阵的形式,一般情况下是二维的。单通道灰度图一般存放的是<uchar>类型,其数据存放格式如下:

    多通道的图像中,每列并列存放通道数量的子列,如RGB三通道彩色图:

    注意通道的顺序为BGR。通常在内存足够大的情况下,图像的每一行是连续存放的,亦即在内存上图像的所有数据组成一个一维向量,这种情况下,在访问时将更快捷。可用成员函数isContinuous()来判断Mat图像在内存中是否为连续存储的。

    清楚了图像在内存中的存储方式,下面对像素值操作:在只对深度为8bit字节型图像操作的前提下,导入一幅彩色图像,将其像素值变换为对255的补数。如原像素值为55,则变换为255-55=200。

    使用一映射表来完成该转换:

    1     uchar mapTable[256];//mapping table:mapTable[pixel_value_before]=255-pixel_value_before;
    2     for (int i = 0; i < 256; i++)
    3         mapTable[i] = 255 - i;

    数组mapTable中装入的即是原像素值变换后的像素值。

    可用如下方法对像素值进行操作:

    1、指针的方式

     1 //通过ptr和[]访问像素的值
     2 void transformImageMethodOne(Mat& pSrcImg, const uchar* const table)
     3 {
     4     CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
     5     int nchannels = pSrcImg.channels();
     6     int nrows = pSrcImg.rows;//矩阵的行数
     7     int ncols = pSrcImg.cols*nchannels;//矩阵的总列数=列数*nchannels
     8     if (pSrcImg.isContinuous())//isContinuous()函数用于判断矩阵是否连续,若连续,相当于只需要遍历一个一维数组
     9     {
    10         cout << "only one row!" << endl;
    11         ncols *= nrows;
    12         nrows = 1;//一维数组
    13     }
    14     //traverse pixel values
    15     for (int i = 0; i < nrows; i++)
    16     {
    17         uchar* ptr = pSrcImg.ptr<uchar>(i);//获取行地址
    18         for (int j = 0; j < ncols; j++)
    19             ptr[j] = table[ptr[j]];//修改像素值
    20     }
    21 //    return pSrcImg;
    22 }

    如代码所示,我们获取每一行开始处的指针,然后遍历至该行末尾。如果矩阵是连续存储的,则只需请求一次指针即可。

    2、或者,也可以借助Mat的成员data。

    data会从Mat中返回指向矩阵的首地址。通过遍历data来扫描整个图像。具体操作如下:

     1 //通过ptr和[]访问像素的值(使用到了Mat的成员data)
     2 void transformImageMethodTwo(Mat& pSrcImg, const uchar* const table)
     3 {
     4     CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
     5     int nchannels = pSrcImg.channels();
     6     int nrows = pSrcImg.rows;
     7     int ncols = pSrcImg.cols*nchannels;
     8     //traver pixel values
     9     uchar* ptr = pSrcImg.data;//指向矩阵的首地址
    10     for (int i = 0; i < ncols*nrows; i++)
    11         *ptr++ = table[*ptr];//*ptr++是先取出*p的值,然后让p++
    12 
    13 }

    3、使用迭代器

    使用迭代器的方法,仅仅需要获得图像矩阵的begin和end,然后从begin迭代至end,将操作符*添加至迭代指针前,即可访问当前指向的内容。

     1 //使用迭代器访问像素的值
     2 void transformImageMethodThree(Mat& pSrcImg, const uchar* const table)
     3 {
     4     CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
     5     const int nchannels = pSrcImg.channels();
     6     switch (nchannels)
     7     {
     8     case 1:
     9     {
    10         MatIterator_<uchar> it, end;
    11         for (it = pSrcImg.begin<uchar>(), end = pSrcImg.end<uchar>(); it != end; it++)
    12             *it = table[*it];
    13         break;
    14     }
    15     case 3:
    16     {
    17               MatIterator_<Vec3b> it, end;
    18               for (it = pSrcImg.begin<Vec3b>(), end = pSrcImg.end<Vec3b>(); it != end; it++)
    19               {
    20                   (*it)[0] = table[(*it)[0]];
    21                   (*it)[1] = table[(*it)[1]];
    22                   (*it)[2] = table[(*it)[2]];
    23               }
    24               break;
    25     }
    26     default:break;
    27     }
    28 
    29 }

    注意:这里对3通道的图像进行操作的时候,使用到了Vec3b。Vec3b作为一个对三元向量的数据结构,用在这里正好是能够表示RGB的三个分量。如果对于彩色图像,仍然使用uchar的话,则只能获得3通道中的B分量

    4、动态地址访问

    这种方法在需要连续扫描所有点的应用场景时效率较低,因为它更适用于随机访问。这种方法最基本的用途是访问任意的某一行某一列:

     1 //使用动态地址访问像素的值
     2 void transformImageMethodFour(Mat& pSrcImg, const uchar* const table)
     3 {
     4     CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
     5     const int nchannels = pSrcImg.channels();
     6     const int nrows = pSrcImg.rows;
     7     const int ncols = pSrcImg.cols;
     8     switch (nchannels)
     9     {
    10     case 1:
    11     {
    12               for (int i = 0; i < nrows;i++)
    13               for (int j = 0; j < ncols; j++)
    14                   pSrcImg.at<uchar>(i, j) = table[pSrcImg.at<uchar>(i, j)];
    15               break;
    16     }
    17     case 3:
    18     {
    19               Mat_<Vec3b> _pSrcImg = pSrcImg;
    20               for (int i = 0; i < nrows;i++)
    21                 for (int j = 0; j < ncols; j++)
    22                 {
    23                     _pSrcImg(i, j)[0] = table[_pSrcImg(i, j)[0]];
    24                     _pSrcImg(i, j)[1] = table[_pSrcImg(i, j)[1]];
    25                     _pSrcImg(i, j)[2] = table[_pSrcImg(i, j)[2]];
    26                 }
    27 //                pSrcImg = _pSrcImg;
    28                 break;
    29     }
    30     default:break;
    31     }
    32 }

    测试代码:

      1 /*
      2 @author:CodingMengmeng
      3 @theme:read the image pixel values by Mat
      4 @time:2017-3-16 23:06:40
      5 @blog:http://www.cnblogs.com/codingmengmeng/
      6 */
      7 #include <cv.h>  
      8 #include <highgui.h>  
      9 using namespace std;
     10 using namespace cv;
     11 
     12 //通过ptr和[]访问像素的值
     13 void transformImageMethodOne(Mat& pSrcImg, const uchar* const table)
     14 {
     15     CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
     16     int nchannels = pSrcImg.channels();
     17     int nrows = pSrcImg.rows;//矩阵的行数
     18     int ncols = pSrcImg.cols*nchannels;//矩阵的总列数=列数*nchannels
     19     if (pSrcImg.isContinuous())//isContinuous()函数用于判断矩阵是否连续,若连续,相当于只需要遍历一个一维数组
     20     {
     21         cout << "only one row!" << endl;
     22         ncols *= nrows;
     23         nrows = 1;//一维数组
     24     }
     25     //traverse pixel values
     26     for (int i = 0; i < nrows; i++)
     27     {
     28         uchar* ptr = pSrcImg.ptr<uchar>(i);//获取行地址
     29         for (int j = 0; j < ncols; j++)
     30             ptr[j] = table[ptr[j]];//修改像素值
     31     }
     32 //    return pSrcImg;
     33 }
     34 //通过ptr和[]访问像素的值(使用到了Mat的成员data)
     35 void transformImageMethodTwo(Mat& pSrcImg, const uchar* const table)
     36 {
     37     CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
     38     int nchannels = pSrcImg.channels();
     39     int nrows = pSrcImg.rows;
     40     int ncols = pSrcImg.cols*nchannels;
     41     //traver pixel values
     42     uchar* ptr = pSrcImg.data;//指向矩阵的首地址
     43     for (int i = 0; i < ncols*nrows; i++)
     44         *ptr++ = table[*ptr];//*ptr++是先取出*p的值,然后让p++
     45 
     46 }
     47 //使用迭代器访问像素的值
     48 void transformImageMethodThree(Mat& pSrcImg, const uchar* const table)
     49 {
     50     CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
     51     const int nchannels = pSrcImg.channels();
     52     switch (nchannels)
     53     {
     54     case 1:
     55     {
     56         MatIterator_<uchar> it, end;
     57         for (it = pSrcImg.begin<uchar>(), end = pSrcImg.end<uchar>(); it != end; it++)
     58             *it = table[*it];
     59         break;
     60     }
     61     case 3:
     62     {
     63               MatIterator_<Vec3b> it, end;
     64               for (it = pSrcImg.begin<Vec3b>(), end = pSrcImg.end<Vec3b>(); it != end; it++)
     65               {
     66                   (*it)[0] = table[(*it)[0]];
     67                   (*it)[1] = table[(*it)[1]];
     68                   (*it)[2] = table[(*it)[2]];
     69               }
     70               break;
     71     }
     72     default:break;
     73     }
     74 
     75 }
     76 //使用动态地址访问像素的值
     77 void transformImageMethodFour(Mat& pSrcImg, const uchar* const table)
     78 {
     79     CV_Assert(pSrcImg.depth() != sizeof(uchar));//if the statement is false,then return a wrong message
     80     const int nchannels = pSrcImg.channels();
     81     const int nrows = pSrcImg.rows;
     82     const int ncols = pSrcImg.cols;
     83     switch (nchannels)
     84     {
     85     case 1:
     86     {
     87               for (int i = 0; i < nrows;i++)
     88               for (int j = 0; j < ncols; j++)
     89                   pSrcImg.at<uchar>(i, j) = table[pSrcImg.at<uchar>(i, j)];
     90               break;
     91     }
     92     case 3:
     93     {
     94               Mat_<Vec3b> _pSrcImg = pSrcImg;
     95               for (int i = 0; i < nrows;i++)
     96                 for (int j = 0; j < ncols; j++)
     97                 {
     98                     _pSrcImg(i, j)[0] = table[_pSrcImg(i, j)[0]];
     99                     _pSrcImg(i, j)[1] = table[_pSrcImg(i, j)[1]];
    100                     _pSrcImg(i, j)[2] = table[_pSrcImg(i, j)[2]];
    101                 }
    102 //                pSrcImg = _pSrcImg;
    103                 break;
    104     }
    105     default:break;
    106     }
    107 }
    108 int main(void)
    109 {
    110     string imgName = "Route66.jpg";
    111     Mat img = imread(imgName);
    112     Mat imgCopy1 = img.clone();
    113     Mat imgCopy2 = img.clone();
    114     Mat imgCopy3 = img.clone();
    115     Mat imgCopy4 = img.clone();
    116     uchar mapTable[256];//mapping table:mapTable[pixel_value_before]=255-pixel_value_before;
    117     for (int i = 0; i < 256; i++)
    118         mapTable[i] = 255 - i;
    119     imshow("SRCIMAGE", img);
    120     transformImageMethodOne(imgCopy1, mapTable);
    121     imshow("TRANSFORMIMAGE_USE_METHOD1", imgCopy1);
    122     transformImageMethodTwo(imgCopy2, mapTable);
    123     imshow("TRANSFORMIMAGE_USE_METHOD2", imgCopy2);
    124     transformImageMethodThree(imgCopy3, mapTable);
    125     imshow("TRANSFORMIMAGE_USE_METHOD3", imgCopy3);
    126     transformImageMethodFour(imgCopy4, mapTable);
    127     imshow("TRANSFORMIMAGE_USE_METHOD4", imgCopy4);
    128     waitKey(0);
    129     return 0;
    130 }

    运行结果:

    原图:

    变换效果图:

    以上。

  • 相关阅读:
    Ubuntu – vlan & bridge configuration
    Python 腾讯云宝塔面板 B站获取经验脚本 部署 自动发邮件
    struts1的总结
    在oracle 中自己所做的作业。。
    昨天上课学到的东西
    今天关于一些学习了国际化的知识
    学习的第二个框架hibernate
    我学习struts的一些心得!
    hibernate的几种状态的详解
    关于hibernate的,另外一些应用!
  • 原文地址:https://www.cnblogs.com/codingmengmeng/p/6560677.html
Copyright © 2011-2022 走看看