Using get() and put() to access pixel values in OpenCV for Java

31,874

Solution 1

It was happening because of byte() casting. I changed the data type of mat image in second case to *CV_64FC3* so that I can use double[] instead of byte[] and it solved the problem.

Mat A = Highgui.imread(image_addr); //"image_addr" is the address of the image
Mat C = A.clone();
A.convertTo(A, CvType.CV_64FC3); // New line added. 
int size = (int) (A.total() * A.channels());
double[] temp = new double[size]; // use double[] instead of byte[]
A.get(0, 0, temp);
for (int i = 0; i < size; i++)
   temp[i] = (temp[i] / 2);  // no more casting required.
C.put(0, 0, temp);

FYI, I also did some time measurement and using second method is way faster than first method.

Solution 2

Found a simple and working solution after a lot of searching-

Mat img = Highgui.imread("Input.jpg"); //Reads image from the file system and puts into matrix
int rows = img.rows(); //Calculates number of rows
int cols = img.cols(); //Calculates number of columns
int ch = img.channels(); //Calculates number of channels (Grayscale: 1, RGB: 3, etc.)

for (int i=0; i<rows; i++)
{
    for (int j=0; j<cols; j++)
    {
        double[] data = img.get(i, j); //Stores element in an array
        for (int k = 0; k < ch; k++) //Runs for the available number of channels
        {
            data[k] = data[k] * 2; //Pixel modification done here
        }
        img.put(i, j, data); //Puts element back into matrix
    }
}
Highgui.imwrite("Output.jpg", img); //Writes image back to the file system using values of the modified matrix

Note: An important point that has not been mentioned anywhere online is that the method put does not write pixels onto Input.jpg. It merely updates the values of the matrix img. Therefore, the above code does not alter anything in the input image. For producing a visible output, the matrix img needs to be written onto a file i.e., Output.jpg in this case. Also, using img.get(i, j) seems to be a better way of handling the matrix elements rather than using the accepted solution above as this helps in visualizing and working with the image matrix in a better way and does not require a large contiguous memory allocation.

Share:
31,874
gargsl
Author by

gargsl

Updated on July 16, 2022

Comments

  • gargsl
    gargsl almost 2 years

    I am a beginner in using OpenCV for JAVA. I want to access individual pixel values of an image matrix. Since, JAVA jar for OpenCV doesn't offer nice functions like C++, I ran into some trouble. After lot of searching, I found out two different methods to do that though they are not explained properly (not even in documentation). We can do that either using get() and put() functions or by converting the mat data into a primitive java type such as arrays. I tried both but getting different output results! Please help explaining what am I doing wrong. Am I using them wrong or some other silly problem. I am still a newbie so please forgive if its a stupid question. :)

    CASE 1: Using get() function

    Mat A = Highgui.imread(image_addr); \\"image_addr" is the address of the image
    Mat C = A.clone();
    Size sizeA = A.size();
    for (int i = 0; i < sizeA.height; i++)
        for (int j = 0; j < sizeA.width; j++) {
            double[] data = A.get(i, j);
            data[0] = data[0] / 2;
            data[1] = data[1] / 2;
            data[2] = data[2] / 2;
            C.put(i, j, data);
        }
    

    CASE 2: Using Array

    Mat A = Highgui.imread(image_addr); \\"image_addr" is the address of the image
    Mat C = A.clone();
    int size = (int) (A.total() * A.channels());
    byte[] temp = new byte[size];
    A.get(0, 0, temp);
    for (int i = 0; i < size; i++)
       temp[i] = (byte) (temp[i] / 2);
    C.put(0, 0, temp);
    

    Now according to my understanding they both should do the same thing. They both access the individual pixel values (all 3 channels) and making it half. I am getting no error after running. But, the output image I am getting is different in these two cases. Can someone please explain what is the issue? May be I don't understand exactly how get() function works? Is it because of the byte() casting? Please help.

    Thanks!

  • rmaik
    rmaik about 9 years
    thanks for the good question, can u please explain why u used : (A.total() * A.channels());?? the total( method returns the withd*height, why u multipled by the channel numbers, i know the RGB image has 3 channels...but i need more clarification please
  • gargsl
    gargsl about 9 years
    @rmaik Each channel has it's own intensity values (red, blue or green) for each pixel. So, total number of those intensity values in an image is equal to number of pixels * total number of channels. So, in this case total() gives you total number of pixels in the image and channel() gives you number of channels.
  • Renaud
    Renaud over 7 years
    It works but is very very slow. Can take 30sec for a 8MP image on a good computer. Instead you want to read all the data at once in a buffer using Mat.get(0,0, byte[totalNumberOfPixel * bytesPerPixel])