OpenCV关于图像处理的预备知识

一. 操作像素

0. 基本概念

  • Scalar表示像素点的值(实际上是个结构体),Scalar(0)表示将该像素点的像素值设为0(单通道)
  • 图像由矩阵表示(数组存储,一维,二维,三维)
  • 矩阵有维度 —dims
  • 每个维度上的每个点表示像素点 — elemSize() 表示每个像素点占的字节数,total()获得总的像素点个数
  • 像素点有通道(单通道或多通道,多通道最多为4) — channels() 表示每个像素点所占的通道总数(灰度图就是单通道,彩色图就是三通道的)
  • 每个通道又由相应的位数来表示该通道上颜色的深浅 — CV_16SC1表示单通道,每个通道用16位(2个字节)表示,S表示有符号整数,C后面数字表示通道数;CV_64FC3表示三通道,每个通道用64位(8字节)浮点数表示)
  • data:存放矩阵数据内存中的首地址
  • step: 是一个数组,表示总字节数,step[0]表示第二个维度上包含的字节数,,step[1]表示第三维度上包含的字节数,以此类推…
  • step1:是函数,step(0)表示总通道数,step1(0)表示第二维度上的总的通道数,step1(1)表示第三维度上总的通道数
  • elemSize:每个像素点的总的字节数
  • elemSize1:每个通道的字节数
  • depth:每个像素的位数,取值为0到6

enum { CV8U=0, CV8S=1, CV16U=2, CV16S=3, CV32S=4, CV32F=5, CV_64F=6 }

  • 图片
  • 取像素点的各个通道值
    单通道: img.at<uchar>(i,j)
    三通道: img.at<Vec3b>(j,i)[0]
           img.at<Vec3b>(j,i)[1]
           img.at<Vec3b>(j,i)[2]
    
  • 图像按颜色分类:
    二值图:图像颜色只有黑白两种构成 无符号位的单通道
    灰度图:图像颜色由白到黑色之间颜色构成 无符号位的单通道
    彩色图:图像颜色由bgr三原色构成的各种颜色构成 三通道(四通道带有透明度)
    

1. 载入,显示,保存图像

  • imread(“图像名”,int flag);
    flag:
        \>0:返回3通道的彩色图 特例:如果原图为黑白图,flag=2时,返回的还是黑白图非彩色图
        =0:返回灰度图
        <0:返回原图(带alpha通道) 
    
  • imwrite(“文件名”,要保存图像的Mat对象);
  • imshow(“图像名”,Mat对象);

2. 使用迭代器遍历图像

Mat_<Vec3b>::interator it = image.begin<Vec3b>();
Mat_<Vec3b>:interator itenfd = image.end<Vec3b>();
for(;it!=itend;++it){
    (*it)[0] = (*it)[0]/div*div+div/2;
    (*it)[1] = (*it)[1]/div*div+dov/2;
    (*it)[2] = (it)[2]/div*div+div/23;
}

3.遍历图像和邻域操作

应用实例: 图像的锐化

锐化公式: sharpened_pixel = 5*current-left-right-up-down; //该位置的像素值的5倍减去与其相邻的点的像素值,必须是上下左右都有像素

核矩阵:

图像滤波器:

4. 颜色缩减

  • 例如:CV_8UC3 — 三通道的八位无符号整数表示像素值,像素值一共有256256256种可能

倘若一幅图包含这些像素值,存储起来是很浪费空间的

因此需要缩减颜色种类的数量,也就是像素值的所有可能取值的情况和

  • 具体的颜色缩减的策略:

5. 图像掩模(或掩模)

  • 为8位单通道图像(灰度图或二值图)
  • 掩码某个位置为0,则在此位置上操作不起作用, 掩码某个位置不为0,则在此位置上操作起作用
  • 可以用来提取不规则ROI
  • 示例代码
    #include "opencv2/opencv.hpp"
    using namespace cv;
    /*
    掩码的使用
        掩码: 8位单通道图像
    
    从一幅图中选取感兴趣区域,使用掩码(形状是根据掩码的非零像素形状决定的)
        可以先创建一个像素值全为零的 Mat对象,然后在图像中画长方形,圆形,多边形等 (设置该区域的像素值为255) 作为掩码(即要操作的区域)
    
        如果 按照掩码操作后 得到图像的边缘有问题 ,肯定是边缘部分的黑色不够完善,则此时需要二值化操作,将边缘部分一律变黑
    
        把多通道的图像怎么读成单通道的图像的原理
    
    */
    void main() {
        Mat img = imread("a.png");//背景
        Mat girlImg = imread("girl.jpg");//前景女孩
        Mat mask = imread("mask.jpg",0);//掩码
        Mat mask1 = Mat::zeros(girlImg.size(), CV_8UC1);
        Mat logo = imread("opencv.jpg");
        Mat mask2 = imread("opencv.jpg", 0);
    
        //图像逻辑相加 中 使用 掩码 ,像素值为0的区域不用考虑
        Mat img1 = imread("3.jpg");
        Mat img2 = imread("4.jpg");
        Mat mask3 = Mat::ones(img1.size(), CV_8UC1);
        circle(mask3, Point(mask3.cols / 2, mask3.rows / 2), 100, Scalar(0), -1, 8);
        Mat dst1 = img2.clone();
        add(img1, img2, dst1,mask3);
        namedWindow("dst1", CV_WINDOW_NORMAL);
        imshow("dst1", dst1);
    
    
    
        circle(mask1, Point(mask.rows / 2, mask.cols / 2), 100,Scalar(255), -1, 8);
        //imwrite("mask1,jpg", mask1);
    
        //对mask2像素值取反,255变为0,变成黑色的掩码区域即不用考虑了
        bitwise_not(mask2, mask2);
        namedWindow("mask2",CV_WINDOW_NORMAL);
        imshow("mask2", mask2);
    
        //二值化取反后的mask2, 为了边缘处理
        threshold(mask2, mask2, 100, 255, THRESH_BINARY);
        namedWindow("二值化后的mask2",CV_WINDOW_NORMAL);
        imshow("二值化后的mask2",mask2);
    
        //在背景图片中选取 两块感兴趣区域
        Mat imgROI = img(Rect(106, 128, girlImg.cols, girlImg.rows));
        Mat imgROI1 = img(Rect(0, 0, logo.cols, logo.rows));
    
        //把两张前景图片按照掩码的方式 复制到 感兴趣区域中
        girlImg.copyTo(imgROI,mask1);
        logo.copyTo(imgROI1,mask2);//复制非黑色区域的部分
    
        imshow("dsr", img);
        imwrite("result.png", img);
        waitKey(0);
    }
    
  • 运行结果

运行结果图 mask2二值化后的mask2dst1

6. ROI(感兴趣区域) — regin of interest

  • 可以只对感兴趣区域进行图像的处理,例如图像的覆盖,区域图像颜色改变
  • 在某一幅图中选出感兴趣区域
    Mat img;
    Mat imgROI;
    imgOROI = img(Rec(左上角横坐标,左上角纵坐标,宽度,高度);
    或者 imgROI = img(Range(起始行数,终止行数).Range(起始列数,终止列数));
    
    imgROI = img.rowRange(start,end);//创建包含原始图像的特定行
    imgROI = img.colRange(start,end);//创建包含原始图像的特定列
    
    row(i) 是 rowRange(start,end)的特例,返回的是Mat对象
    col(j) 是 colRange(start,end)的特例,返回的是Mat对象
    
  • 在原图中选出感兴趣区域,并用logo这张图进行添加
    1.像素值相加处理
    Mat imgROI;//声明感兴趣区域
    Mat img;//原图像
    imgROI = img(Rec(0,0,logo.cols,logo.rows));//在原图像抠出感兴趣区域(矩形)
    addWeighted(imgROI,1.0,logo,0.3,0.,imgROI);//在感兴趣区域与logo图像直接相加(可能出现像素饱和)
    
    2.像素值的替换
    imgROI = img(Rec(23,34,logo.cols,logo.rows));//声明感兴趣区域
    Mat mask = immread("logo.bmp",0);//加载掩模.必须是灰度图
    logo.copyTo(imgROI,mask);//通过掩模拷贝ROI
    

 

7. 图像的运算

  • 算术运算
    • +
      dst = img1 + img2;
      
    • add()
      add(img1,img2,dst);
      
    • addWeighted()
      addWeighted(img1,0.5,img2,0.5,0,dst);//0.5是各自图像像素值的比重
      
    • dst = img1 -img2;
      
    • substract()
      substract(img1, img1,dst); 像素差小于零按零计算
      
    • absdiff()
      absdiff(img1,img2,dst); 像素差按绝对值计算
      
    • *
      dst = A * img1;
      
    • /
      dst = img1 /A;
      
  • 逻辑运算
    • bitwise_and(srcImg1,srcImg2,outImg,mask);
    • bitwise_or(srcImg1,srcImg2,outImg,mask);
    • bitwise_not(srcImg,outImg,mask);
    • bitwise_xor(srcImg1,srcImg2,outImg,mask);//异或运算,各自图像中独有的部分的并集
posted @ 2017-04-23 22:15  勇武的加兰  阅读(494)  评论(0编辑  收藏  举报