OpenCV学习笔记之扫描图像

一、 图像在内存中存储方式

图像矩阵的大小取决于我们所用的颜色模型,确切地说,取决于所用通道数。如果是灰度图像,矩阵就会像这样:

\newcommand{\tabItG}[1] { \textcolor{black}{#1} \cellcolor[gray]{0.8}} \begin{tabular} {ccccc} ~ & \multicolumn{1}{c}{Column 0} &   \multicolumn{1}{c}{Column 1} &   \multicolumn{1}{c}{Column ...} & \multicolumn{1}{c}{Column m}\\ Row 0 & \tabItG{0,0} & \tabItG{0,1} & \tabItG{...}  & \tabItG{0, m} \\ Row 1 & \tabItG{1,0} & \tabItG{1,1} & \tabItG{...}  & \tabItG{1, m} \\ Row ... & \tabItG{...,0} & \tabItG{...,1} & \tabItG{...} & \tabItG{..., m} \\ Row n & \tabItG{n,0} & \tabItG{n,1} & \tabItG{n,...} & \tabItG{n, m} \\ \end{tabular}

而对多通道图像来说,矩阵中的列会包含多个子列,其子列个数与通道数相等。例如,RGB颜色模型的矩阵:

\newcommand{\tabIt}[1] { \textcolor{yellow}{#1} \cellcolor{blue} &  \textcolor{black}{#1} \cellcolor{green} & \textcolor{black}{#1} \cellcolor{red}} \begin{tabular} {ccccccccccccc} ~ & \multicolumn{3}{c}{Column 0} &   \multicolumn{3}{c}{Column 1} &   \multicolumn{3}{c}{Column ...} & \multicolumn{3}{c}{Column m}\\ Row 0 & \tabIt{0,0} & \tabIt{0,1} & \tabIt{...}  & \tabIt{0, m} \\ Row 1 & \tabIt{1,0} & \tabIt{1,1} & \tabIt{...}  & \tabIt{1, m} \\ Row ... & \tabIt{...,0} & \tabIt{...,1} & \tabIt{...} & \tabIt{..., m} \\ Row n & \tabIt{n,0} & \tabIt{n,1} & \tabIt{n,...} & \tabIt{n, m} \\ \end{tabular}

注意到,子列的通道顺序是反过来的:BGR而不是RGB。很多情况下,因为内存足够大,可实现连续存储,因此,图像中的各行就能一行一行地连接起来,形成一个长行。连续存储有助于提升图像扫描速度,我们可以使用 isContinuous() 来去判断矩阵是否是连续存储的. 相关示例会在接下来的内容中提供。

二、扫描图像几种方式

2.1 指针访问

/*!
 * \brief scanImagePtr
 * \param image
 * \return
 */
Mat& scanImagePtr(Mat &image){
    double start = static_cast<double>(getTickCount());
    //! 行
    int rowNumber = image.rows;
    //! 每一行元素个数 = 列数 x 通道数
    int colNumber = image.cols * image.channels();
    for (int i = 0; i < rowNumber; i++)
    {
        //! 获取第i行的首地址
        uchar* data = image.ptr<uchar>(i);
        for (int j = 0; j < colNumber; j++)
        {
            data[j] /= 2;
        }
    }
    double end = static_cast<double>(getTickCount());
    double time = (end - start) / getTickFrequency();
    cout << "type1:" << time << "s" << endl;
    return image;
}

2.2 迭代器iterator

/*!
 * \brief scanImageiterator
 * \param image
 * \return
 */
Mat& scanImageiterator(Mat &image){
    double start = static_cast<double>(getTickCount());
    //! 初始位置的迭代器
    Mat_<Vec3b>::iterator it = image.begin<Vec3b>();
    //! 终止位置的迭代器
    Mat_<Vec3b>::iterator itend = image.end<Vec3b>();
    for (; it != itend; it++)
    {
        //! 处理BGR三个通道
        (*it)[0] = 255;//B
        //(*it)[1] = 255;//G
        //(*it)[2] = 0;//R
    }
    double end = static_cast<double>(getTickCount());
    double time = (end - start) / getTickFrequency();//计算时间
    cout << "type2:" << time << "s" << endl;
    return image;
}

2.3 动态地址计算

/*!
 * \brief scanImageAt
 * \param image
 * \return
 */
Mat& scanImageAt(Mat &image){
    double start = static_cast<double>(getTickCount());
    int rowNumber = image.rows;
    int colNumber = image.cols;
    for (int i = 0; i < rowNumber; i++)
        for (int j = 0; j < colNumber; j++)
        {
            //! 处理BGR三个通道
            //image.at<Vec3b>(i, j)[0] = 0;//B
            image.at<Vec3b>(i, j)[1] = 255;//G
            //image.at<Vec3b>(i, j)[2] = 0;//R
        }
    double end = static_cast<double>(getTickCount());
    double time = (end - start) / getTickFrequency();//计算时间
    cout << "type3:" << time << "s" << endl;
    return image;
}

2.4 三种方法效能对比

type1:0.000189559s
type2:0.000832323s
type3:0.000424337s

 经典的C风格运算符[](指针)访问要更胜一筹。

代码详见《OpenCV扫描图像三种方式练习

三、OpenCv坐标系

OpenCV中坐标体系中的零点坐标定义为图片的左上角,X轴为图像矩形的上面那条水平线,从左往右;Y轴为图像矩形左边的那条垂直线,从上往下。在Point(x,y)和Rect(x,y)中,第一个参数x代表的是元素所在图像的列数,第二个参数y代表的是元素所在图像的行数,而在at(x,y)中是相反的。

参考资料:

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值