OpenCV Learning (Pixel operation manipuating the Pixels)

Source: Internet
Author: User
Tags rand advantage
OpenCV Learning (Pixel operation manipuating the Pixels)

OpenCV Although there are many types of image processing functions, you can do a variety of common image processing, but there will always be some operations are not, then we need to manipulate the pixels, to achieve the functions we need. Today we will talk about several methods of OpenCV for pixel-level operation, and make a comparison.

In OpenCV, the image is represented by a matrix, and the corresponding data type is Cv::mat. Cv::mat function is very powerful, the elements of the matrix can be byte, word, floating point number, array and other forms. For grayscale images, each pixel is represented by a 8 bit byte, for a color image, each pixel is an array of three elements, each of which stores the BGR component, where everyone is wrong, that is BGR instead of RGB, three bytes per pixel, the first byte is the blue component, Don't ask me why I designed this, I don't know. Accessing a single pixel (at function)

The Cv::mat class has an at (int y, int x) method that can access a single pixel. But we know that Cv::mat can store various types of images. You must specify the type of pixels to return when calling this function, because the AT function is a template function.

In the case of grayscale, we know that pixels are stored in the form of unsigned character-type variables. Then visit as follows.

Image.at<uchar> (j,i) = value;

If the image is a 24-bit true color, then you can:

Image.at<cv::vec3b> (j,i) [channel]= value;

Here is a simple example of opening a color image and randomly adding some noise to it. The original image is as follows:

The Core code:

Cv::mat image = Cv::imread ("q:\\test.jpg", Cv_load_image_color);
for (int k = 0; k < k++)
{
    int i = rand ()% Image.cols;
    Int j = rand ()% Image.rows;
    Image.at<cv::vec3b> (i, J) [0] = 255;
    Image.at<cv::vec3b> (i, J) [1] = 255;
    Image.at<cv::vec3b> (i, J) [2] = 255;
}

The image is processed as follows:

It's tedious to specify the type each time you use the AT function, like above. You can then take advantage of Cv::mat's derived class, Cv::mat_ class, which is a template class. When you create an instance of this class, you specify the type, and then you do not need to specify the type each time you use it. Here is an example.

Cv::mat_ <cv::Vec3b> ima = image;
Cv::namedwindow ("Origin image", cv::window_normal);
Cv::imshow ("Origin image", image);

for (int k = 0; k < k++)
{
    int i = rand ()% Ima.cols;
    Int j = rand ()% Ima.rows;
    Ima (i, J) [0] = 255;
    Ima (i, j) [1] = 255;
    Ima (i, J) [2] = 255;
}

The effect is the same after the code is processed.

In the program above, there is a

IMA = image;

One of the features of OpenCV is that ordinary matrix copy operations are called shallow copies. This means that the IMA and image share the same image data after this operation.

If we want to really replicate the image data. You can then use the Clone () method. Code similar to the following:

Cv::mat_ <cv::Vec3b> ima = Image.clone ();

Then the IMA and image are completely independent. traversing image pixels with pointers

Often, our algorithm needs to traverse all the pixels of the image. At this point the at function will be very slow. The more efficient way to access image pixels is by using pointers.

To put it simply, we usually get the head pointer of a row of pixels. If the image is grayscale, it is similar to this operation.

uchar* data = image.ptr<uchar> (j);

If the image is 24-bit colored, you can do this:

CV::VEC3B * data = image.ptr<cv::vec3b> (j);

In fact, even a color image can be pointed to with a uchar-type pointer. As long as we're on our own. Calculates the position offset of the pixel to be accessed relative to the beginning of the line. For example, the following function, which can handle grayscale images, can also handle color images to reduce the color used in the image.

void Colorreduce (Cv::mat &image, int div=64)
{
    int nl = image.rows;//number of lines
    //Total number of Elements per line
    int NC = Image.cols * image.channels ();
    for (int j = 0; J < NL; j + +) 
    {
        //Get the address of row J
        uchar* Data= image.ptr<uchar> (j);
        for (int i = 0, i < NC; i++) 
        {
            //process each pixel---------------------
            data[i]= data[i]/div * div + DIV/2;
            End of pixel processing----------------
        }
    }//End of Line
}

The results from applying the default parameters to our test images are as follows:

There is a line in the code above that is used to calculate how many bytes are in a row of pixels. The premise, of course, is that each channel occupies a single byte.

int nc= Image.cols * image.channels ();

If each channel takes up more than one byte, the formula above is wrong, and we can calculate this.

int NC = Image.cols * image.elemsize ();

Image.elemsize () Gets the number of bytes per pixel. Multiply the number of pixels in a row by exactly how many bytes are in a row.

In the example above, we use a double loop to iterate through each pixel in the image. In fact, because we're doing the same thing for each pixel, we don't need to determine which line a pixel is. So the code above can be further optimized, with only one loop to complete.

However, we have to pay special attention to the memory space of some images that are not contiguous. After a row of pixels ends, a small chunk of memory may be vacated. This is because some CPU instructions have memory requirements for the data. This is a waste of space, but it will be faster to operate.

Whether the memory occupied by the image is contiguous can be obtained using iscontinuous (). If it is contiguous, you can use a loop to finish processing all the pixels. Here is the code, which takes into account both continuous and discontinuous memory, which degrades into two loops when memory is not contiguous:

void ColorReduce2 (Cv::mat &image, int div=64) 
{
    int nl = image.rows;//number of lines
    int NC = Image.col S * image.channels ();
    if (image.iscontinuous ())
    {
        //Then no padded pixels
        NC = NC * NL;
        NL = 1; It is now a 1D array
    }
    //This loop is executed only once
    //In case of continuous images for
    (int J = 0; J < NL; J + +) 
    {
        uchar* data = image.ptr<uchar> (j);
        for (int i = 0, i < NC; i++) 
        {
            //process each pixel---------------------
            data[i] = data[i]/div * DIV + DIV/2;
            End of pixel processing----------------
        }//End of Line
    }
}

If we want to get the first address of the image data, we can do the following:

Uchar *data = Image.data;

For two-dimensional image data, the number of bytes occupied by each row of images is stored by the member variable step. So:

Data + = Image.step;

Causes data to point to the first memory address of the next line of images.

Of course, these operations are relatively low-level pointer operations, not recommended. using iterators to traverse image data

Iterator is used extensively in the Standard Template Library (STL) of C + +. OpenCV also imitates STL to show his own set of iterator.

The Cv::matiterator_ class is designed in OpenCV, which is similar to Cv::mat_ and is also a template class. You need to specify a specific type when instantiating this class. For example, the following code:

Cv::matiterator_<cv::vec3b> it;

Another way to use this is as follows:

Cv::mat_<cv::vec3b>::iterator it;

If we just use iterator to read the pixel value without changing it, we can use the constant type iterator.

Cv::matconstiterator_<cv::vec3b> it;
Cv::mat_<cv::vec3b>::const_iterator it;

The above example is rewritten with iterator after the code is as follows:

void ColorReduce3 (Cv::mat &image, int div=64)
{
    //obtain iterator at initial position
    CV::MAT_<CV:: Vec3b>::iterator it = image.begin<cv::vec3b> ();
    Obtain end position
    cv::mat_<cv::vec3b>::iterator itend = image.end<cv::vec3b> ();
    Loop over all pixels for
    (; it!= itend; ++it)
    {
        //process pixel---------------------
        (*it) [0] = (*it) [0]/div * div + div/2;
        (*it) [1] = (*it) [1]/div * div + div/2;
        (*it) [2] = (*it) [2]/div * div + div/2;
    }
}

There are pros and cons to this approach, and the biggest drawback is that this code can only handle 24-bit true color images. The advantage is that there is no need to focus on memory continuity. In contrast, code that uses iterator is slower than a direct pointer operation. speed Comparison of various methods

The above describes several methods of accessing image pixels, without regard to efficiency, these methods are very good, can achieve the same function. But in computer vision applications, computational efficiency (operating speed) is often a key factor that we have to consider.

So here's a special section to compare the speed of each method.
For completeness, a colorreduce function that accesses pixels with an at function is also given below.

void ColorReduce0 (Cv::mat &image, int div=64)
{
    int nl = image.rows;//number of lines
    int NC = Image.col s;

    for (int j=0; j<nl; j + +)
    {for
        (int i=0; i<nc; i++)
        {
            //process pixel---------------------
  image.at<cv::vec3b> (j,i) [0]=
                    image.at<cv::vec3b> (j,i) [0]/div*div + div/2;
            Image.at<cv::vec3b> (j,i) [1]=
                    image.at<cv::vec3b> (j,i) [1]/div*div + div/2;
            Image.at<cv::vec3b> (j,i) [2]=
                    image.at<cv::vec3b> (j,i) [2]/div*div + div/2;
            End of pixel processing----------------
        }//End of Line
    }
}

Look back at some of the ways we've implemented it. COLORREDUCE0 (): Use the AT function to access the Pixel ColorReduce1 (): Double loop, with pointer access to Pixel COLORREDUCE2 (): When the image memory is contiguous, use a heavy loop to access all Pixels colorReduce3 (): With iterator accessing pixels

Use Cv::gettickcount () to calculate the elapsed time of each function. ColorReduce0 (): 240579 ColorReduce1 (): 22363 colorReduce2 (): 21202 colorReduce3 (): 77573

The results at a glance, ColorReduce0 run the slowest, followed by ColorReduce3.

ColorReduce1 and ColorReduce2 are not much different. Therefore, when we write a program, we should use ColorReduce2 or ColorReduce1 as much as possible.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.