VideoCapture from webcam stuck in black and white instead of color

OpenCV version 4.11.0, python library opencv-python-headless = "^4.11.0.86"

I am using the following code to capture a single still from a USB webcam and to write it to a file. I am expecting that the .jpg be in color as the webcam captures in color, however the image is always black and white:

#!/usr/bin/env python

import time
from pathlib import Path

import cv2

capture = cv2.VideoCapture(0, cv2.CAP_V4L2)
capture.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
capture.set(cv2.CAP_PROP_FRAME_WIDTH, 240)
capture.set(cv2.CAP_PROP_SATURATION, 0.9)

while not capture.isOpened():
    print('webcam not ready yet')
    time.sleep(0.5)

success, frame = capture.read()
if not success:
    raise RuntimeError('no frame')

frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
success, data = cv2.imencode('.jpg', frame_rgb)
if not success:
    raise RuntimeError('could not convert to jpg')

with open(Path.home() / 'test.jpg', 'wb') as f:
    f.write(data.tobytes())


capture.release()

If I dump out frame.shape I get (480, 640, 3) so it seems like it is capturing in BGR color space but it’s only getting black and white data.

Does anyone know what is wrong with this code or my setup?

1 Like

maybe it’s that. have you looked into that?

and why would you do that? if not for the ruined saturation above crushing all the colors, you’d see flipped colors now.

just imwrite(), passing str(some_pathlib_path) and you’re good.

just assert that and fail if it’s not right. unless this case actually happens, and unless the sleep actually fixes the situation, then there’s no reason to sleep and no reason to continue. usually, after the constructor returns, something that isn’t open won’t become open a moment later.

Hello, thank you for the suggestions. On the suggestion of how to save the file with imwrite: I should have been more clear that this source code is extracted from a larger context that needs to generate a bytearray of jpg data w/o any file io. In this example I have it write to a file so I can visually inspect it, but the actual application will stream it over the network which is harder to inspect when there are problems at this stage. Should have phrased this better to avoid an X Y question.

If I use the imwrite approach that works and I get a color image. Is there a way to do imwrite to a Python BytesIO so I can avoid file ops? Looks like from a google search that most people use imencode which would bring me back to my current situation.

Using the imencode approach without the saturation property set still results in a black and white image for both frame and frame_rgb

then you should investigate whatever you’re doing to transmit and decode that byte stream.

I don’t think it’s a transmitting or decoding problem because at the point of

with open(Path.home() / 'test.jpg', 'wb') as f:
    f.write(data.tobytes())

The image that is being written to disk, for debugging purposes, is in black and white, and the code above that part is the same as what’s in the streaming application. The original python snippet is just extracted and the transmit part is replaced with the f.write() part just to sanity check what is being sent over the network–so I know that before I send the data that the data I have in the numpy array is in black and white.

I’ve found it, the issue was on my side and not OpenCV related. I’m guessing that the low saturation value was persisted from the original version of the script? I think that the gain was set to a really high value because sometimes I would accidentally run the program while the webcam cap was still on.

v4l2-ctl -d /dev/video0 --list-ctrls
                     saturation 0x00980902 (int)    : min=0 max=255 step=1 default=128 value=5
        white_balance_automatic 0x0098090c (bool)   : default=1 value=1
                           gain 0x00980913 (int)    : min=0 max=255 step=1 default=0 value=255

Looks much better now and is in color

                     brightness 0x00980900 (int)    : min=0 max=255 step=1 default=128 value=128
                       contrast 0x00980901 (int)    : min=0 max=255 step=1 default=128 value=128
                     saturation 0x00980902 (int)    : min=0 max=255 step=1 default=128 value=128
        white_balance_automatic 0x0098090c (bool)   : default=1 value=1
                           gain 0x00980913 (int)    : min=0 max=255 step=1 default=0 value=109
1 Like

I have noticed that too. either the camera or the driver in the operating system persists those things. they are not reset to any defaults when you “open” it. they might reset with a system reboot, or they might require the device to be unplugged, or maybe neither would do that and it’s stored on the device (unlikely) or by the system.

1 Like