为了编写计算机视觉应用,我们必须会存取图像的内容,如修改或者创建图像,这些过程都需要对图像的基本元素进行操作,即所谓的像素。

1【存取像素值】

为了存取矩阵元素,我们需要在代码中指定元素所在的行和列,程序会返回相应的元素。如果是单通道的,返回值是单个数值;如果是多通道的,返回值是一组向量(Vector)。

我们先通过一个简单的例子演示如何直接存取像素值。在图像随机添加一些椒盐噪点,随机将一些像素设置为白色或者黑色。在传输过程中,如果部分像素值丢失,那么这种噪点就会出现。首先写一个简单的函数

void salt(cv::Mat&image, int n)
{
    for(int i = 0; i < n; i++)
    {
        int k = qrand() % image.cols;
        int j = qrand() % image.rows;
        if(image.channels() == 1)
        {
            image.at<uchar>(j, k) = 255;
        }
        else if(image.channels() == 3)
        {
            image.at<cv::Vec3b>(j, k)[0] = 255;
            image.at<cv::Vec3b>(j, k)[1] = 255;
            image.at<cv::Vec3b>(j, k)[2] = 255;
        }
    }

}

函数功能很简单,就不介绍了。

测试程序如下

cv::Mat image = cv::imread("timg.jpg");
    cv::namedWindow("Test1");
    cv::imshow("Test1", image);

salt(image, 30000);
    cv::namedWindow("Test2");
    cv::imshow("Test2", image);

效果如下所示

2【通过指针遍历图像】

通过一个颜色缩减函数来说明指针遍历图像的方法。主要函数如下

void colorReduce(const cv::Mat& image, cv::Mat& result, int div = 64)
{
    result.create(image.rows, image.cols, image.type());
    int nl = image.rows;
    int nc = image.cols * image.channels();
    if (image.isContinuous())
    {
        nc = nc * nl;
        nl = 1;
    }

for(int i = 0; i < nl; i++)
    {
        const uchar* data_in = image.ptr<uchar>(i);
        uchar* data_out = result.ptr<uchar>(i);
        for (int j = 0; j < nc; j++)
        {
            data_out[j] = data_in[j]/div * div + div / 2;
        }
    }

}

运行效果如下所示

成员变量cols代表图像的列数(宽度),rows代表图像的行数(高度),step代表以字节为单位的图像的有效宽度。即使你的图像的元素类型不是uchar,step仍然代表着行的字节数。像素的大小可以油elemSize函数得到:对于一个三通道的short行矩阵(CV_16SC3),elemSize返回6。图像的通道数可以由channels方法得到:对于灰度图像来说为1,对于彩色图像来说为3。total函数返回矩阵的像素个数。一般出于效率考虑,每行会填补一些额外像素,这是因为,如果行的长度为4或者8的倍数,对于一些处理器来说,处理的更高效。如果需要根据新的尺寸和数据类型对一个矩阵进行重新分配,我们可以调用create成员函数。。而且,如果新指定的尺寸和数据类型与原有的一样,create函数会直接返回,不会对本矩阵做任何改动。图像分配内存的大小为total()*elemSize()。

3【使用迭代器遍历图像】

opencv为cv::Mat提供了与STL迭代器兼容的迭代器。可以通过cv::Mat Iteerator_的实例来得到。这是一个模板类。主要函数程序如下:

void colorIteratorReduce( cv::Mat& image, cv::Mat& result, int div = 64)
{
    result.create(image.rows, image.cols, image.type());
    cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>();
    cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>();
    cv::Mat_<cv::Vec3b>::iterator rit = result.begin<cv::Vec3b>();
    for(; it != itend; it++)
    {
        (*rit)[0] = (*it)[0]/div * div + div /2 ;
        (*rit)[1] = (*it)[1] / div * div + div / 2;
        (*rit)[2] = (*it)[2] / div * div + div / 2;
        rit++;
    }

}

运行效果和上一个效果一样。image.begin<cv::Vec3b>()来得到图像左上角位置的迭代器,也可以通过对迭代器进行代数运算,但是,end方法得到的迭代器其实已经超出了集合。迭代器之所以可以这样创建,是因为一个cv::Mat_引用在创建的时候就隐士声明了迭代器的类型,这样避免在使用begin和end方法的时候还要指定迭代器的类型。

二【遍历图像和邻域操作】

在图像处理中,通过当前位置的相邻像素计算新的像素位置是很常见的操作,当邻域包含图像的前几行和下几行是,就需要同时扫描图像的若干行。本例子对图像进行锐化,它是基于拉普拉斯算子(在后面章节会介绍)。本例子是减去前后左右的像素来锐化。主要代码如下

void sharpen(const cv::Mat& image, cv::Mat&result)
{
    result.create(image.rows, image.cols, image.type());
    int nl = image.rows - 1 ;
    int cl = (image.cols  - 1) * image.channels() ;
    std::cout<<"muyi"<<cl;

for(int j = 1; j < nl ; j++)
    {
        const uchar* previous = image.ptr<const uchar>(j - 1);
        const uchar* current = image.ptr<const uchar>(j);
        const uchar* next = image.ptr<const uchar>( j + 1);
        uchar* outPut = result.ptr<uchar>(j);
        for(int i = 1; i < cl; i++)
        {
             *outPut++ = cv::saturate_cast<uchar>(5 * current[i] - current[i - 1] - current [ i + 1] - previous[i] - next[i]);
        }
    }
    result.row(0).setTo(cv::Scalar(0,0,0));
    result.row(nl).setTo(cv::Scalar(0,0,0));
    result.col(0).setTo(cv::Scalar(0,0,0));
    result.col(image.cols - 1).setTo(cv::Scalar(0,0,0));
}

运行效果如下

cv::saturate_cast被用来对计算结果进行截断。使用setTo函数来设置矩阵的值,这个函数会将矩阵的所有元素设为指定的值。cv::Scalar(a,b,c)来指定像素三个通道的目标值。

opencv(二)图像像素提取及操作相关推荐

  1. Python-OpenCV对图像像素的遍历操作示例

    Python-OpenCV对图像像素的遍历操作示例 如果您想了解OpenCV-C++是如何遍历图像像图的,那么可以参看下面这个页面: https://www.hhai.cc/thread-110-1- ...

  2. 有关两个图像像素一一对应相乘操作

    有关两个图像像素一一对应相乘操作 有时候会 需要将两幅图对应的像素点进行相乘,用img1 * img 2 程序不会出现问题,但是最后结果会出现问题. 问题有可能是这样的: #这里的两幅图,im1 为原 ...

  3. dataset中获取图像的名字_GDAL与OpenCV的图像像素读取格式互相转换

    前言 任务原因,遇到了一个需要GDAL图像像素格式与OpenCV相互转换的问题,解决之后顺便记录一下. 正文 OpenCV(Open Source Computer Vision Library)是一 ...

  4. pythonopencv提取圆内图像_python – 使用OpenCV从图像中提取多边形给定...

    使用cv2.fillConvexPoly以便您可以指定2D点阵列并定义一个蒙版,该蒙版填充由这些点定义的形状在蒙版中为白色.如果多边形中定义的点是凸的(因此名称为fillConvexPoly),则应该 ...

  5. Opencv -- 18图像像素类型转换与归一化

    原文笔记参考(转载)链接:https://www.freesion.com/article/48551470037/. 什么是归一化 概念一:归一化是把需要处理的数据通过某种算法处理后限制在所需要的一 ...

  6. 图像像素的算数操作-5

    一.代码部分 (1)头文件中声明函数 #pragma once #include<opencv2/opencv.hpp>using namespace cv;class QuickDemo ...

  7. opencv之 图像像素操作(如何通过opencv在图像上划线)

    首先我们了解,一个图像,都是一个大的二维数组,每一个元素都是一个像素,若是rgb图像,则该像素还会有rgb三个值,我们读入一个图像 import cv2img = cv2.imread("o ...

  8. OpenCV(二)图像的载入,显示,和输出

    一.OpenCV的命名空间 在写简单的OpenCV程序的时候,以下两句话是标配: #include<opencv2/opencv.hpp> using namespace cv; 二.关于 ...

  9. 我的Qt作品(7)使用Qt+OpenCV实现图像轮廓提取,再用三阶贝塞尔曲线拟合成光滑线条/多边形拟合

    一.贝塞尔曲线基础知识 给一系列顶点,如果只是用直线将其中的各个点依次连接起来,最终形成一个折线图,这种很容易实现.但是现实中事物的变化往往具有连续的特性,即使是给定了一系列离散的点,基于以往的生活经 ...

最新文章

  1. python wxpython plt_wxpython和多个面板中的多个plt图
  2. 所谓的创业分享,都是一堆骗人骗己的谎言?
  3. BPF Tools 参考链接
  4. 线性代数的问题:是否存在这样的矩阵,它满足正交对角化的条件,但它不是实对称矩阵呢?
  5. python PyQt5 QtWidgets.QAbstractSlider.valueChanged()
  6. powershell 开发入门_详谈Ubuntu PowerShell(小白入门必看教程)
  7. mysql设置定位慢查询_mysql优化——定位慢查询
  8. 1008. Elevator (20)
  9. 已通过os信号请求关闭服务器,redis(一)内部机制的介绍和启动过程
  10. 一个简单的完成端口(服务端/客户端)类
  11. Unity3D shader Blending
  12. span标签居中显示的正确方法
  13. matlab zxing ean13,【zxing】Ean13 Contents do not pass checksum
  14. rtmp代理php源码_RTMP直播系统(示例代码)
  15. 什么是哑终端,终端模拟器
  16. 路径中“./”、“../”、“/”代表的含义 路径相关知识点总结
  17. vue之设置背景图片自适应屏幕
  18. 怎样缓解灾难性遗忘?持续学习最新综述三篇
  19. VSC学习前端,不容错过的插件
  20. OCR文字识别软件怎样操作

热门文章

  1. JVM 字节码从入门到精通
  2. ubuntu 12.04下安装adobe flash
  3. Frida Hook Android App 进阶用法之 Java 运行时
  4. 看网易云公开课代码性能开发有感,怎样提高代码的性能
  5. Java平时练习(一)
  6. html邢台模板,邢台水泥预制件定制
  7. Android第三方SDK集成 —— 极光推送
  8. 游戏数字资产复用——有哪些是你需要知道的?
  9. 前端项目如何使用svg矢量图
  10. 气传导耳机和骨传导耳机的区别是啥?气传导耳机有哪些优缺点?