Efficient scanning of continuous images
We previously explained that, for efficiency reasons, an image can be padded with extra pixels at the end of each row. However, it is interesting to note that when the image is unpadded, this one can also be seen as a long one-dimensional array of WxH pixels. A convenient cv::Mat method can tell us whether the image has been padded. It is the isContinuous method that returns true if the image does not include padded pixels. Note that we could also check the continuity of the matrix by writing the following test:
// check if size of a line (in bytes) // equals the number of columns times pixel size in bytes image.step == image.cols*image.elemSize();
To be complete, this test should also check whether the matrix has only one line, in which case, it is continuous by definition. Nevertheless, always use the isContinuous method to test the continuity condition. In some specific processing algorithms, you can take advantage of the continuity of the image by processing it in one single (longer) loop. Our processing function would then be written as follows:
void colorReduce(cv::Mat &image, int div=64) { int nl= image.rows; // number of lines int nc= image.cols * image.channels(); if (image.isContinuous()) { // then no padded pixels nc= nc*nl; nl= 1; // it is now a long 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 } }
Now when the continuity test tells us that the image does not contain padded pixels, we eliminate the outer loop by setting the width to 1 and the height to WxH. Note that there is also a reshape method that could have been used here. You would write the following in that case:
if (image.isContinuous()) { // no padded pixels image.reshape(1, // new number of channels 1); // new number of rows } int nl= image.rows; // number of lines int nc= image.cols * image.channels();
The reshape method changes the matrix dimensions without requiring any memory copying or reallocation. The first parameter is the new number of channels and the second one is the new number of rows. The number of columns is readjusted accordingly.
In these implementations, the inner loop processes all image pixels in a sequence. This approach is mainly advantageous when several small images are scanned simultaneously into the same loop.