Due to the nature of the algorithms we are testing for our thesis, we had to “prototype” the procedures in Matlab so that we can easily modify parameters and test variables. However, since Matlab is expensive and we are a university that does not tolerate piracy ;), we used GNU Octave, a FOSS equivalent of Matlab (think Mono for C#).
We are done with the algorithm-prototyping part and we are now porting our Matlab code to Java, since this thesis is meant to be used by scientists, with a GUI and all that comes with standard software. A big part of this task is in coding the functions that are built-in in Matlab; remember that Matlab is meant specially for mathematical purposes (it is a portmanteau of Matrix Laboratory) while Java is more general purpose, closer to metal, if you will.
For the past few days, I’ve been trying to implement the Matlab function rgb2gray which, as the name suggests, converts an RGB-colored image to grayscale. Now, there are a lot of ways to convert an image to grayscale but getting a grayscale isn’t the main point here. Getting it the way Matlab/Octave does is essential so that we can recreate in Java the recognition accuracy we achieved in Octave. We will be manipulating these pixel values after all.
So, I looked into Matlab’s documentation of rgb2gray and found that, for a given RGB pixel, it gets the the corresponding grayscale value by the following weighted sum:
0.2989 * R + 0.5870 * G + 0.1140 * B
(Or something close to those constants/giving the same priority over the RGB components. That is, green most weighted, followed by red, and then blue. This priority reflects the sensitivity of the human eye to these colors. See Luma (video).)
I then ran some tests on Octave to verify the docs:
octave3.2:1> four = imread("four.JPG"); octave3.2:2> four(1,1,1) # The red component of the first pixel of four.JPG ans = 159 octave3.2:3> four(1,1,2) # The green component of the first pixel of four.JPG ans = 125 octave3.2:4> four(1,1,3) # The blue component of the first pixel of four.JPG ans = 64 octave3.2:5> grayval = 0.2989 * 159 + 0.5870 * 125 + 0.1140 * 64 grayval = 128.20 |
So, the grayscale equivalent of the first pixel of four.JPG will have the value floor(128.20)=128. Sure enough, when I encoded the procedure in Java, the first pixel of the grayscaled four.JPG has the value 127—close enough taking into account the possible differences in how Java and Octave handle floating point computation.
But wait, there’s more…
octave3.2:6> fourgray = rgb2gray(four); octave3.2:7> fourgray(1,1) ans = 116 |
The value of the first pixel of four.JPG after rgb2gray is 116! Now that’s something no amount of discrepancy in floating-point handling can account for. Besides, hasn’t Octave itself computed a value close to Java’s 127 when done manually?
That’s when I realized that Octave may not be an exact port/clone of Matlab after all. I decided to Google “rgb2gray octave” and, sure enough, the documentation of Octave at SourceForge points to a departure from Matlab’s implementation:
Function File: gray = rgb2gray (rgb)
…
If the input is an RGB image, the conversion to a gray image is computed as the mean value of the color channels.
And verifying the docs…
octave3.2:8> floor((159 + 125 + 64)/3) ans = 116 |
Problem solved.
I’m pretty sure that this isn’t the only difference between Matlab and Octave. The next time I encounter another one, I’ll try to document it here, time permitting.
BONUS: My encounters with Octave so far gives credence to this but I have yet to verify this thoroughly. It seems that Matlab/Octave loops through matrices (arrays of at least two dimensions, in Java/C-speak) column-major order. This isn’t exactly difficult to do in Java/C but Java/C programmers are more used to traversing multidimensional arrays in row-major order, since this should result to less page faults and therefore faster code. Still, for some computations, the order with which you traverse a matrix matters a lot. Be careful there.
2 Comments
Hi,
What has your experience been with porting Matlab/Octave to Java? How easy/difficult was it, and did you use any porting libraries or code converters?
Joris
Hi Joris,
The difficulty depends on how much of Matlab/Octave do you want in Java and your skill level as well. If you are adept at numerical algorithms (NA) this task shouldn’t take too long. If you’re not that confident with NA, some algorithms Matlab/Octave uses is still pretty basic (matrix operation, for one) that any programmer with reasonable skill should be able to cope with them.
When I wrote this, my team was unable to find any third-party code/library that might help so we did the port ourselves. Search around first, a lot could’ve changed since then. Finally, advice from my present self in attempting this task is, write unit tests! (My team had something akin to unit tests when we did this but nothing as formal as JUnit. Do use a unit test library!)
Good luck.
One Trackback/Pingback
[…] time, I told you how we are porting all our Matlab/Octave code to Java. I wrote about a huge difference between Matlab and Octave’s implementation of rgb2gray. However, as it became apparent to me, my issues with rgb2gray are not quite done […]