Search code examples
c++opencvcomputer-visionruntime-errorpixel

OpenCV: Error copying one image to another


I am trying to copy one image to another pixel by pixel (I know there are sophisticated methods available. I am trying to solve another problem and answer to this will be useful).

This is my code:

int main()
{
    Mat Img;
    Img = imread("../../../stereo_images/left01.jpg");

    Mat copyImg = Mat::zeros(Img.size(), CV_8U);
    for(int i=0; i<Img.rows; i++){
        for(int j=0; j<Img.cols; j++){
            copyImg.at<uchar>(j,i) = Img.at<uchar>(j,i);
        }}
    namedWindow("Image", CV_WINDOW_AUTOSIZE );
    imshow("Image", Img);
    namedWindow("copyImage", CV_WINDOW_AUTOSIZE );
    imshow("copyImage", copyImg);
    waitKey(0);
    return 0;
}

When I run this code in visual studio I get the following error

OpenCV Error: Assertion failed (dims <= 2 && data && (unsigned)i0 < (unsigned)si
ze.p[0] && (unsigned)(i1*DataType<_Tp>::channels) < (unsigned)(size.p[1]*channel
s()) && ((((sizeof(size_t)<<28)|0x8442211) >> ((DataType<_Tp>::depth) & ((1 << 3
) - 1))*4) & 15) == elemSize1()) in cv::Mat::at, file c:\opencv\opencv-2.4.9\ope
ncv\build\include\opencv2\core\mat.hpp, line 537

I know for fact that Img's type is CV_8U. Why does this happen ?

Thanks!


Solution

  • I know for fact that Img's type is CV_8U.

    But CV_8U is just the image depth (8-bit U-nsigned). The type also specifies the number of channels, which is usually three. One for blue, one for green and one for red in this order as default for OpenCV. The type would be CV_8UC3 (C-hannels = 3). imread will convert even a black and white image to a 3-channel image by default. imread(filename, CV_LOAD_IMAGE_GRAYSCALE) will load a 1-channel image (CV_8UC1). But if you're not sure the easiest solution is

    Mat copyImg = Mat::zeros(Img.size(), Img.type());
    

    To access the array elements you have to know the size of it. Using .at<uchar>() on a 3-channel image will only access the first channel because you have 3*8 bit per pixel. So on a 3-channel image you have to use

    copyImg.at<Vec3b>(i,j) = Img.at<Vec3b>(i,j);
    

    where Vec3b is a cv::Vec<uchar, 3>. You should also note that the first argument of at<>(,) is the index along dim 0 which are the rows and second argument cols. Or in other words in classic 2d-xy-chart order you access a pixel with .at<>(y,x) == .at<>(Point(x,y)).