24-bit color diagram and 8-bit grayscale diagram
First, let’s introduce a 24-bit color image. In a 24-bit color image, each pixel is represented by three bytes, usually expressed as RGB. Generally, many 24-bit color images are stored as 32-bit images, and the excess bytes per pixel are stored as an alpha value, showing special influence information [1].
In the RGB model, if R=G=B, the color represents a grayscale color, where the value of R=G=B is called the grayscale value. Therefore, each pixel of the grayscale image requires only one byte to store the grayscale value (also known as the intensity value and brightness value), and the grayscale range is 0-255[2]. This gives you a grayscale picture of the picture.
Several methods of grayscale
1. Component method: Use one of the three RGB components as the grayscale value of the grayscale map.
2. Most value method: Use the maximum or minimum value of the three RGB components as the grayscale value of the grayscale map.
3. Mean method: Use the average value of the three RGB components as the grayscale value of the grayscale graph.
4. Weighting method: Due to the different color sensitivity of human eyes, weighted average of RGB three-components can obtain a more reasonable grayscale image. The general situation is as follows: Y = 0.30R + 0.59G + 0.11B.
[Note] The weighting method actually takes the brightness value of a picture as the grayscale value to calculate, and uses the YUV model. In [3], you will find that the author used Y = 0.21 * r + 0.71 * g + 0.07 * b to calculate the grayscale value (obviously the sum of the three weights does not equal 1, which may be the author's mistake?). In fact, this difference should be related to whether gamma correction is used [1].
A method for implementing grayscale in Java
If you search for "Java implements grayscale", most of them are one method (code):
public void grayImage() throws IOException{ File file = new File(System.getProperty("user.dir")+"/test.jpg"); BufferedImage image = ImageIO.read(file); int width = image.getWidth(); int height = image.getHeight(); BufferedImage grayImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); for(int i= 0 ; i < width ; i++){ for(int j = 0 ; j < height; j++){ int rgb = image.getRGB(i, j); grayImage.setRGB(i, j, rgb); } } File newFile = new File(System.getProperty("user.dir")+"/method1.jpg"); ImageIO.write(grayImage, "jpg", newFile); }The original image of test.jpg is:
The grayscale diagram obtained using the above method:
Seeing this grayscale image seems to be feasible, but if we use opencv to achieve grayscale or use PIL (Python), you will find that the effects vary greatly:
img = cv2.imread('test.jpg',cv2.IMREAD_COLOR)gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)cv2.imwrite('PythonMethod.jpg', gray) It can be clearly seen that the grayscale graph obtained using opencv (the same is true for PIL) is much better than the method obtained by the above Java method, and many details can be seen. This shows that this popular method on the Internet has always had some problem, but it has been ignored.
How to achieve grayscale in opencv
If you have read books or codes related to opencv , you can probably know that opencv grayscale uses weighting method. The reason is that it is roughly because we don’t know why opencv grayscale images are so good. Are there any other processing details that we ignore?
Verifying our guess is very simple. Just check the changes before and after the pixel value is grayed out. You can test it as follows:
img = cv2.imread('test.jpg',cv2.IMREAD_COLOR)h, w = img.shape[:2]gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) for j in range(w): for i in range(h): print str(i) + " : " + str(j) + " " + str(gray[i][j])print img[h-1][w-1][0:3]It is difficult for us to judge so many pixels below, but we only need to pay attention to the last pixel point and we can find clues: The RGB value of the last pixel point in the original image is 44, 67, 89, and the value after graying is 71. It just fits the grayscale value calculated by the weighting method. If you check the pixel values of the image that was grayed out in Java before, you will find that not only the pixel values do not meet this formula, but are even far from each other.
At this point, we guess that opencv (including PIL) is a grayscale implementation using weighting method.
Java implements weighted method grayscale
If that popular method on the Internet doesn't work, how should we use Java to achieve grayscale? In fact, [3] has successfully achieved (multiple methods) grayscale (foreign friends are still very powerful in technology), and only the necessary code is extracted here:
private static int colorToRGB(int alpha, int red, int green, int blue) { int newPixel = 0; newPixel += alpha; newPixel = newPixel << 8; newPixel += red; newPixel = newPixel << 8; newPixel += green; newPixel = newPixel << 8; newPixel += blue; return newPixel; }public static void main(String[] args) throws IOException { BufferedImage bufferedImage = ImageIO.read(new File(System.getProperty("user.dir" + "/test.jpg")); BufferedImage grayImage = new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(), bufferedImage.getType()); for (int i = 0; i < bufferedImage.getWidth(); i++) { for (int j = 0; j < bufferedImage.getHeight(); j++) { final int color = bufferedImage.getRGB(i, j); final int r = (color >> 16) & 0xff; final int g = (color >> 8) & 0xff; final int b = color & 0xff; int gray = (int) (0.3 * r + 0.59 * g + 0.11 * b);; System.out.println(i + " : " + j + " " + gray); int newPixel = colorToRGB(255, gray, gray, gray); grayImage.setRGB(i, j, newPixel); } } File newFile = new File(System.getProperty("user.dir") + "/ok.jpg"); ImageIO.write(grayImage, "jpg", newFile);} The above code will print out the grayed-scale pixel values. If you compare them with the above Python code, you will find that the pixel values are completely corresponding. The colorToRGB method handles the color diagram exactly 4 bytes, one of which is the alpha parameter (as mentioned earlier). The following figure is the grayscale image of this code:
For other methods, the same can be obtained in turn.
Summarize
The reason for this article is to use Java to implement several grayscale operations and use opencv to verify the right or wrong of the conversion. However, some problems were found in actual tests (there are differences in the converted pictures, and how to generate grayscale images based on the grayscale values after conversion), and some thoughts and verifications were made on this. It should be noted here that some articles on the Internet have more or less done further thinking (and many of them are even copied, especially domestic articles), and for these practical issues, hands-on implementation and verification is a very important method. I hope the content of this article will be helpful to everyone. If you have any questions, please leave a message to discuss.