gfxenon

导航

 

这几天开始看OpenCV。

基本是跟着官方的tutorial来的,目前已经看到更改图像对比度和亮度。

之前在研究生阶段上过一门机器视觉的课,当时老师为了尽快讲明原理,使用的是Matlab+Simulink。

后来找了一个创业公司实习,创业的主要产品就是一个基于机器视觉的一个图像对比的产品。

这次算是重新系统的学习一下,也当做是练手的东西,提升一下自己实际的变成技巧以及对细节的把控能力。

总结一下这几天看到的和记住的内容。

opencv基础操作:

1. 打开图像

Mat image;
image = imread(argv[1], IMREAD_COLOR);

Mat是OpenCV里面应用最广泛的一个数据容器,本质上是一个矩阵。用来存放各种图像。完成图像的运算。

现在用到的构造函数有以下几个:

    //Create matrix using explicit construction function.
    Mat M(2, 2, CV_8UC3, Scalar(0, 0, 255));
    cout << "M=" << endl << " " << M << endl;
    //Create matrix with more than 2 dimensions.
    int sz[3] = { 2, 2, 2 };
    Mat L(3, sz, CV_8UC(1), Scalar::all(0));
    //cout << "L=" << endl << L << endl;
    //Warning: Matrix with more than 2 dimension can not be printed with cout!
    IplImage *img = cvLoadImage("opencv-logo.png", 1);
    Mat matx1(img);
  Mat C = (Mat_<double>(3,2) << 0, 1, 0, -1, 5, -1);

Mat储存图像时需要用户来设定的包括图像大小,像素通道数,像素深度。

图像大小就是图像各个方向上的像素数量。通道数是指每个像素使用多少个数值来表示,比如灰度图像只需一个数值,RGB彩色图像需要3个数值。值得注意的是,Mat当中每个像素的3个数据代表的含义是BGR,和人们经常说的RGB正好反过来。

图像深度暂时还没搞懂,大概是一个表述每个像素点的数值占据空间大小的这么个东西。

图像深度、图像通道数可以通过几个固定的宏来传递给构造函数。形式是CV_8UC3。8代表图像深度为8个bit,U代表unsigned char。C3代表3通道。

其他还有CV_32FC3等。看其他文章,这个数据在处理图像、储存图像时需要特别注意一下。

最后一种构造方法比较奇特,使用了C++当中stream来获取矩阵元素。

Mat使用方便,有些人也用它来进行矩阵运算。

另外Mat可以不止2个维度,可以任意设定,这个也是他设计比较好的以体现。

imread函数可以直接打开opencv兼容的图像,返回给Mat对象。

    namedWindow("Original Picture", WINDOW_AUTOSIZE);
    namedWindow("Processed Picture", WINDOW_AUTOSIZE);
    //imshow("Display window", edges);
    //system("pause");
    imshow("Original Picture", image);
    imshow("Processed Picture", edges);

下面是图像的显示,windows下封装成2个函数,非常简单。

值得注意的是namedWindow的第二个参数,可以设定图像是否随着窗口的大小改变大小。

然后是几个Matlab风格的矩阵。

  Mat E = Mat::eye(3, 3, CV_32F);
    Mat O = Mat::ones(2, 2, CV_64FC1);
    Mat Z = Mat::zeros(3, 3, CV_8UC3);
    cout << "E=" << endl << E << endl;
    cout << "O=" << endl << O << endl;
    cout << "Z=" << endl << Z << endl;

分别是单位矩阵、全1矩阵、全0矩阵。

然后是引用矩阵的行,或者列。

  //Create new matrix using row function an clone function.
    //Row index begin with 0. Like the C/C++ style arrays.
    Mat rowmat = C.row(1).clone();
    cout << "rowmat = " << endl << rowmat << endl;

然后是randu函数,可以给矩阵当中每个元素一个随机值,随机值的范围可以设定。可以用来生成白噪声?

  //randu function. Fill a matrix with random value. The upper limit and lower limit can be set.
    Mat R(4, 4, CV_32SC3);
    randu(R, Scalar::all(-100), Scalar::all(100));
    cout << "R = " << endl << R << endl;

打印Mat时,可以使用format函数来设定不同的输出格式。

    //Different output styles.
    //Notice the numpy styles. It has a additional element to mark the data type of the matrix.
    cout << "R(default) = " << endl << R << endl;
    cout << "R(Python) = " << endl << format(R, "python") << endl;
    cout << "R(CSV) = " << endl << format(R, "csv") << endl;
    cout << "R(numpy) = " << endl << format(R, "numpy") << endl;
    cout << "R(c) = " << endl << format(R, "c") << endl;

有python风格、numpy风格、CSV风格等。值得注意的是numpy风格时会自动输出矩阵当中元素类型。

//isContinuous function. Return if the matrix is stored as a single row in memory.
if (R.isContinuous())
{
cout << "R is stored in memory as a single big row." << endl;
}
else
{
cout << "R is stored in memory not as a single big row." << endl;
}

这个isContinuous函数来获取矩阵的储存方式信息,如果矩阵是一一个大一维数据储存在内存里,那么就是true。

这个函数在判断矩阵储存方式之后,可以使得后面的循环便利矩阵变得方便。

 

    //Output other opencv datas.
    //Point2F p1(3, 4);
    //cout << "Point2F p1 = " << endl << p1 << endl;
    //Initialize a matrix with a C++ vector. The matrix will be initialized to a column vector.
    vector<float> v1;
    v1.push_back((float)CV_PI);
    v1.push_back(2);
    v1.push_back(3);
    cout << "Mat(v1) = " << endl << Mat(v1) << endl;

在一个是使用C++当中vector来初始化矩阵,初始化之后的矩阵是一个列向量。

 

LookUpTable用来把原有图像进行单个像素级别的映射,通俗的说就是每个像素的新的值只由它自己决定,和他的邻居无关。因为色彩空间是有限的,所以可以通过查表来加快速度,而不需要使用Filter2D。

LUT(M, lookUpTable, T);

他的3个参数都是Mat对象。第一个是输入,第二个是映射表,第三个是输出。

比如输入图像时一个uchar 3通道彩色图像,那么映射表应该是一个有256元素的矩阵,1行256列。

LUT可以使用OpenCV的GPU加速还是什么东西,比手撸映射函数要快很对。

 

然后是OpenCV的掩码功能、卷积功能。

首先定义一个掩码或者核,然后遍历整个图像,计算新的图像。

和之前映射表不同的是掩码功能输出的结果是和其他像素有关系, 不只是和它自己有关系。

可以手撸一个掩码函数,也可以利用内置的Filte2D。

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;

void Sharpen(const Mat& in, Mat& result)
{
    CV_Assert(in.depth() == CV_8U);
    result.create(in.size(), in.type());
    int channels = in.channels();
    const uchar *previous, *next, *current;
    uchar* output;
    int rows = in.rows;
    int cols = in.cols;
    for (int i = 1; i < rows - 1; i++)
    {
        previous = in.ptr<uchar>(i - 1);
        current = in.ptr<uchar>(i);
        next = in.ptr<uchar>(i + 1);
        output = result.ptr<uchar>(i);
        for (int j = channels; j < channels * (cols - 1); j++)
        {
            //Conversion of uchar. Be careful!
            //saturate_cast<uchar>(int n):
            //if n > 255, then the result is 255.
            //if n < 0, then the result is 0.
            //This function is used to prevent uchar overflow.
            *output++ = saturate_cast<uchar>(5 * current[j] - current[j - channels] - current[j + channels] - previous[j] - next[j]);
        }
    }
    result.row(0) = Scalar(0);
    result.row(rows - 1) = Scalar(0);
    result.col(0) = Scalar(0);
    result.col(cols - 1) = Scalar(0);
}

int main(int argc, char** argv)
{
    Mat In = imread(argv[1], IMREAD_COLOR);
    if (!In.data)
    {
        cout << "Load image failed!" << endl;
        return -1;
    }
    Mat Result;
    //Sharpen(In, Result);
    Mat kern = (Mat_<char>(3,3) << 0, -1 , 0,
                                    -1, 5 , -1,
                                    0, -1 , 0);
    filter2D(In, Result, In.depth(), kern);
    namedWindow("Original", CV_WINDOW_AUTOSIZE);
    namedWindow("Sharpened", CV_WINDOW_AUTOSIZE);
    imshow("Original", In);
    imshow("Sharpened", Result);
    waitKey(0);
    return 0;
}

另外一个值得注意的是saturate_cast这个东西,简单来说就是在进行从int到uchar转换时让图像不失真,把负数全算作0,大于255的数算作255。

效果还是很显著的。如果不用这个东西就会出现很多花点,想来是截断时让色彩发生了很大失真。

 

这些就是这几天的学习内容。

posted on 2017-12-02 23:07  gfxenon  阅读(229)  评论(0)    收藏  举报