1

I would like to create following 2x2 image from numpy array using OpenCV.

2x2 image that consists of 1x1 tiles with colors blue, green, red and blue

I tried to create it with this code snippet.

import cv2
import numpy as np

blue = (255, 0, 0)
green = (0, 255, 0)
red = (0, 0, 255)

image = np.array([
    [blue, green],
    [red, blue]
])

cv2.imwrite('2x2.jpg', image)

I used BGR color format, yet when I view the output image, I see a colorless image.

colorless 2x2 image

What am I missing?

Environment

  • OpenCV 4.5.1.48
  • Python 3.9.1
  • Windows 10
sanitizedUser
  • 1,723
  • 3
  • 18
  • 33
  • Does [this](https://stackoverflow.com/questions/39316447/opencv-giving-wrong-color-to-colored-images-on-loading) answer your question? – Libra Apr 28 '21 at 21:25
  • 1
    With such a small image, it might be that the `JPEG` encoding is over compressing your original data. Try saving the file as a `PNG` instead, with `cv2.imwrite('2x2.png', image)`. – stateMachine Apr 28 '21 at 22:46
  • 1
    Good point about jpg compression for images smaller than the jpg 8x8 block size. – fmw42 Apr 28 '21 at 23:10
  • 1
    @fmw42 It's not about the size, same happens if you tile this pattern. It's the effect of chroma subsampling used by JPEG. I think 4:2;0 variant (the most common), which would mean that colour is sampled at half the resolution (i.e. one sample for a 2x2 tile). Seems to fit, the result appears to only have varying luminance and same hue. Really shows why JPEG is bad choice for synthetic images like this. – Dan Mašek Apr 29 '21 at 09:29
  • 2
    OpenCV doesn't provide a way to directly control the subsampling method, however there is a side effect -- when you specify both `IMWRITE_JPEG_LUMA_QUALITY` and `IMWRITE_JPEG_CHROMA_QUALITY` and they are not equal, subsampling is turned off: https://github.com/opencv/opencv/blob/3.4/modules/imgcodecs/src/grfmt_jpeg.cpp#L709 | Unfortunately this needs `JPEG_LIB_VERSION >= 70`, and sadly the library shipped with OpenCV and used to build the standard releases (at least on Windows) is only version 62. – Dan Mašek Apr 29 '21 at 09:59
  • 1
    If JPEG format is required, Pillow allows direct control over subsampling: `Image.fromarray(image[...,::-1]).save('2x2_PIL.jpg', quality=100, subsampling=0)` – Dan Mašek Apr 29 '21 at 10:07
  • 1
    Python Wand also allows you to control subsampling. – fmw42 Apr 29 '21 at 15:54

2 Answers2

2

I believe that your issue may be not specifying the dtype for your array. The following works fine for me in Python/OpenCV.

import cv2
import numpy as np

blue = (255, 0, 0)
green = (0, 255, 0)
red = (0, 0, 255)

image = np.array([
    [blue, green],
    [red, blue]
], dtype=np.uint8)

image = cv2.resize(image, (200,200), interpolation = cv2.INTER_AREA)

cv2.imwrite('2x2.jpg', image)

enter image description here

fmw42
  • 46,825
  • 10
  • 62
  • 80
  • This doesn't solve it. When you change the size from `(200, 200)` to `(2, 2)` the output is the same colorless image. Specifying the `dtype` alone does nothing. – sanitizedUser Apr 28 '21 at 21:45
  • It works fine for me. If I remove the dtype, the resize crashes for me with an OpenCV error message. Sorry, if it does not work for you. I am using Python 3.7.5 and OpenCV 3.4.8 – fmw42 Apr 28 '21 at 21:49
  • I use OpenCV 4.5.1.48 and if I remove the dtype it produces the same output. No crashes. – sanitizedUser Apr 28 '21 at 22:04
2

I think the jpg compressing method cause this issue on a few pixel shaped images

Instead of:

cv2.imwrite('2x2.jpg', image)

try to save it as png:

cv2.imwrite('2x2.png', image)

and always use uint8 on image array.

stateMachine
  • 5,227
  • 4
  • 13
  • 29
Norbert Tiborcz
  • 306
  • 2
  • 9