openCV入门

测试环境 xcode8 macOS 10.12.5 openCV 2.4

读取/显示图像

#include <opencv2/opencv.hpp>
using namespace cv;
int main(int argc, const char * argv[]) {        
    showImage2();    
    return 0;
}

void showImage(){    
    //老版本cvLoadImage
    //新版imread
    //为图形分配空间
    IplImage *image = cvLoadImage("/Users/tyrad/Desktop/openCV/aaa.png");
    namedWindow("example");
    cvShowImage("example", image);
    cvWaitKey(0);
    cvReleaseImage(&image);
    cvDestroyWindow("example");
}

void showImage2(){
    cv::Mat image ;
    std::cout << "image.rows = " << image.rows << " \nimage.cols=" <<image.cols << std::endl;    
    image = cv::imread("/Users/tyrad/Desktop/openCV/aaa.png");
    std::cout << "image.rows = " << image.rows << " \nimage.cols=" <<image.cols << std::endl;
    if (image.empty()) {
        printf("未创建图像");
        //未创建图像
        return;
    }
    cv::namedWindow("window");
    cv:imshow("window", image);
    cv::waitKey(0);
}

| | 解释 |
| — | — | | cv::Mat image ; | 创建了空图像,尺寸为0,0: |
|cv::imread("path/to/image");|读取图片| |image = cv::imread("path/to/image", CV_LOAD_IMAGE_GRAYSCALE);|读取文件并转换为灰度图像| |image = cv::imread("path/to/image", CV_LOAD_IMAGE_GRAYSCALE);|读取文件并转换为三通道彩色图像|

绘图

//绘图功能
void drawRect(){    
    cv::Mat image ;
    image = cv::imread("/Users/tyrad/Desktop/lena.jpg", CV_LOAD_IMAGE_GRAYSCALE);
    if (image.empty()) {
        printf("未创建图像");
        return;
    }
    cv::circle(image,
               cv::Point(image.cols/2,image.rows/2),//中心点坐标
               65,  //半径
               0,  //颜色(黑色)
               3); //厚度
    
    cv::putText(image,
                "this is a pic",
                cv::Point(40,200),
                cv::FONT_HERSHEY_PLAIN,
                2.0,
                255,
                2);    
    //其他还有circle/ellipse/line
    cv::namedWindow("window");
    cv:imshow("window", image);
    cv::waitKey(0);
}

201709213718r.png

认识cv::Mat

获取图像大小: cv::Size

新建一个240行320列的新图像:

cv::Mat image1(240,320,CV_8U,100);

CV_8U:表示每个像素对应1字节(u表示无符号) CV_8UC3:表示彩色图像,三通道类型

创建一个灰色图像:

cv::namedWindow("image");
cv::Mat image1(240,320,CV_8U,100);
cv::imshow("image", image1); //显示图像
cv::waitKey(0); //等待按键盘

创建一个红色图像,cv::Scalar用于在调用函数时传递像素值:

cv::namedWindow("image");
cv::Mat image2(240,320,CV_8UC3,cv::Scalar(0,0,255));
cv::imshow("image", image2);
cv::waitKey(0);

create方法用于重新分配图像的数据块:

image1.create(200,200,CV_8U)
void addLogo(){
    
    cv::Mat image = cv::imread("/Users/tyrad/Desktop/lena.jpg");
    cv::Mat logo    = cv::imread("/Users/tyrad/Desktop/juhua.png");

    //图像右下角定义一个ROI, 标注了图片和图片插入的位置
    cv::Mat imageROI(image, cv::Rect(image.cols-logo.cols,image.rows-logo.rows,logo.cols,logo.rows));
    //插入标志
    logo.copyTo(imageROI);
    
    cv::imshow("image", image);
    cv::waitKey(0);
}

20170921242832.png

图像的掩码:

void addLogo(){
    
    cv::Mat image = cv::imread("/Users/tyrad/Desktop/lena.jpg");
    cv::Mat logo    = cv::imread("/Users/tyrad/Desktop/juhua.png");

    cv::Mat imageROI(image, cv::Rect(image.cols-logo.cols,image.rows-logo.rows,logo.cols,logo.rows));
    //将标志作为掩码(必须为灰度图像)
    cv::Mat mask(logo);
    //插入标志,值复制掩码不为0的位置
    logo.copyTo(imageROI,logo);
    
    cv::imshow("image", image);
    cv::waitKey(0);
}

201709212283.png

访问像素值

使用cv::Mat的at(int y,int x)方法可以访问图像矩阵的元素。

因为cv::Mat可以接受任何类型的元素,因此需要我们制定返回值的预期类型(保证指定的类型和矩阵内的类型是一致的)。

彩色图像每个像素对应三个部分:红、绿、蓝。因为cv::Mat类返回一个向量,向量包含三个八位的值。

//访问像素
void salt(cv::Mat image , int n ){
    
    int i, j;
    for (int k = 0;  k < n; k ++) {
        j = std::rand()%image.cols;
        i = std::rand()%image.rows;
        //对图像进行修改加入白色像素颗粒
        if (image.type() == CV_8UC1) { //灰度图像
            //黑白图像直接255
            image.at<uchar>(j,i) = 255;
        }else if(image.type() == CV_8UC3){ //彩色图
            //彩色图像需要将rbg都设置成255
            image.at<cv::Vec3b>(i,j)[0] = 255;
            image.at<cv::Vec3b>(i,j)[1] = 255;
            image.at<cv::Vec3b>(i,j)[2] = 255;
        }
    }
}

void pixCntrol(){
    cv::Mat image = cv::imread("/Users/tyrad/Desktop/lena.jpg");
    salt(image, 500);
    cv::imshow("image", image);
    cv::waitKey(0);
}

2017092112375g.png

指针扫描像素

写一个减色算法: 减少图片颜色的数量,利用整数除法的特性,将一区块的色彩调整到这个区块的中间值。只需要遍历所有的像素值就可以了。

ptr方法可以直接访问图像中的一个行的地址。

2017092232631rdu.png

void myColorReduce(cv::Mat image ,int div=64){
    //行
    int nl = image.rows;
    //每行的元素数量
    int nc = image.cols * image.channels();
    //遍历每行
    for (int j = 0; j < nl; j++) {
        //获取行j的地址
        uchar *data = image.ptr<uchar>(j);
        //遍历每列
        for (int i = 0 ; i < nc; i++) {
            data[i] = data[i]/div*div + div/2;
        }
    }
}
void colorReduce(){
    cv::Mat image = cv::imread("/Users/tyrad/Desktop/lena.jpg");
    myColorReduce(image);
    cv::imshow("image", image);
    cv::waitKey(0);
}

效果

2017092253012r.png

高效的图像扫描循环

简单的图像运算(以图像组合为例)

需要用到addWeighted方法:

cv::Mat image = cv::imread("/Users/tyrad/Desktop/lena.jpg");    
cv::Mat image2    = cv::imread("/Users/tyrad/Desktop/256.jpg");    
if (image.empty()) {
   std::cout<< "Error image"<<std::endl;
}    
if (image2.empty()) {
   std::cout<< "Error image2"<<std::endl;
}    
Mat dst;    
cv::addWeighted(
               image,//图片1
               0.7, //alpha 图片1的融合比例
               image2,//图片2
               0.9, //图片2的融合比例
               0, //偏差
               dst //输出图片
               );    
cv::imshow("image", dst);
cv::waitKey(0); 

需要注意的是需要保证两张图片的大小和类型相同。

201709222884c.png

修改像素位置

使用remap函数,首先定义处理中使用的映射参数,然后把映射参数引用到输入图像。

void wave(const cv::Mat &image , cv::Mat &result ){
    //映射参数,分别反映x,y坐标的变化
    cv::Mat srcX(image.rows, image.cols, CV_32F);
    cv::Mat srcY(image.rows, image.cols, CV_32F);
    //创建映射参数
    for (int i=0; i<image.rows; i++) {
        for (int j=0; j<image.cols; j++) {
            //调整位置关系
            srcX.at<float>(i,j) = j; //列位置不变
            srcY.at<float>(i,j) = i + 5 * sin(j/10.0); //行位置根据正弦函数移动
        }
    }
    cv::remap(image, result, srcX, srcY, cv::INTER_LINEAR);
}

void myWave(){
    
    cv::Mat image = cv::imread("/Users/tyrad/Desktop/lena.jpg");
    cv::Mat result;

    wave(image, result);
    
    cv::imshow("image", result);
    cv::waitKey(0);
}

可以看到图片被变形了:

2017092249396wave.png

待续…