How to set manual white balance temperature in OpenCV (Python)
Using OpenCV on Linux, if you have a video device that interfaces a V4L2 device such as a USB webcam:
in order to set the manual white balance temperature, you first need to disable automatic white balancing using CAP_PROP_AUTO_WB . See our previous post How to enable/disable manual white balance in OpenCV (Python) for more details on how you can do this, here’s only the short version that works with most cameras.
After that, you can set the white balance temperature using CAP_PROP_WB_TEMPERATURE :
camera.set(cv2.CAP_PROP_AUTO_WB, 0.0) # Disable automatic white balance camera.set(cv2.CAP_PROP_WB_TEMPERATURE, 4200) # Set manual white balance temperature to 4200K
For V4L2 cameras, as you can see in our previous post on mapping of OpenCV parameters to V4L2 parameters, CAP_PROP_WB_TEMPERATURE is mapped to V4L2_CID_WHITE_BALANCE_TEMPERATURE which is shown in v4l2-ctl -d /dev/video0 —all as white_balance_temperature
. Therefore, you can easily verify if, for example, disabling the auto white balance worked for your V4L2 camera such as any USB camera by looking at the white_balance_temperature section of v4l2-ctl -d /dev/video0 —all :
white_balance_temperature 0x0098091a (int) : min=2800 max=6500 step=1 default=4600 value=4200
If this post helped you, please consider buying me a coffee or donating via PayPal to support research & publishing of new posts on TechOverflow
7 transformation principles:
The principle of gray world, perfect reflection, dynamic threshold and its C++ implementation:
HDR correction: contrast image correction
7 conversion codes:
Reference:In the realization of several white balances, the speed is optimized, and new white balance algorithms (gamma correction and HDR correction) are added.
# Copyright 2020 chenli Authors. All Rights Reserved. import cv2 import numpy as np import math def mean_white_balance(img): """ The first simple average white balance method :param img: image data read by cv2.imread :return: The returned white balance result image data """ # Read image b, g, r = cv2.split(img) r_avg = cv2.mean(r)[0] g_avg = cv2.mean(g)[0] b_avg = cv2.mean(b)[0] # Find the gain occupied by each channel k = (r_avg + g_avg + b_avg) / 3 kr = k / r_avg kg = k / g_avg kb = k / b_avg r = cv2.addWeighted(src1=r, alpha=kr, src2=0, beta=0, gamma=0) g = cv2.addWeighted(src1=g, alpha=kg, src2=0, beta=0, gamma=0) b = cv2.addWeighted(src1=b, alpha=kb, src2=0, beta=0, gamma=0) balance_img = cv2.merge([b, g, r]) return balance_img def perfect_reflective_white_balance(img_input): """ Perfect reflection white balance STEP 1: Calculate the sum of R\G\B for each pixel STEP 2: According to the value of R+G+B, calculate the value of the previous Ratio% as the reference point threshold T STEP 3: For each point in the image, calculate the average value of the cumulative sum of the R\G\B components of all points where the R+G+B value is greater than T STEP 4: Quantify the pixels to [0,255] for each point Relying on the selection of the ratio value and the image that is not white in the brightest area is not good. :param img: image data read by cv2.imread :return: The returned white balance result image data """ img = img_input.copy() b, g, r = cv2.split(img) m, n, t = img.shape sum_ = np.zeros(b.shape) # for i in range(m): # for j in range(n): # sum_[i][j] = int(b[i][j]) + int(g[i][j]) + int(r[i][j]) sum_ = b.astype(np.int32) + g.astype(np.int32) + r.astype(np.int32) hists, bins = np.histogram(sum_.flatten(), 766, [0, 766]) Y = 765 num, key = 0, 0 ratio = 0.01 while Y >= 0: num += hists[Y] if num > m * n * ratio / 100: key = Y break Y = Y - 1 # sum_b, sum_g, sum_r = 0, 0, 0 # for i in range(m): # for j in range(n): # if sum_[i][j] >= key: # sum_b += b[i][j] # sum_g += g[i][j] # sum_r += r[i][j] # time = time + 1 sum_b = b[sum_ >= key].sum() sum_g = g[sum_ >= key].sum() sum_r = r[sum_ >= key].sum() time = (sum_ >= key).sum() avg_b = sum_b / time avg_g = sum_g / time avg_r = sum_r / time maxvalue = float(np.max(img)) # maxvalue = 255 # for i in range(m): # for j in range(n): # b = int(img[i][j][0]) * maxvalue / int(avg_b) # g = int(img[i][j][1]) * maxvalue / int(avg_g) # r = int(img[i][j][2]) * maxvalue / int(avg_r) # if b > 255: # b = 255 # if b < 0: # b = 0 # if g >255: # g = 255 # if g < 0: # g = 0 # if r >255: # r = 255 # if r < 0: # r = 0 # img[i][j][0] = b # img[i][j][1] = g # img[i][j][2] = r b = img[:, :, 0].astype(np.int32) * maxvalue / int(avg_b) g = img[:, :, 1].astype(np.int32) * maxvalue / int(avg_g) r = img[:, :, 2].astype(np.int32) * maxvalue / int(avg_r) b[b >255] = 255 b[b < 0] = 0 g[g >255] = 255 g[g < 0] = 0 r[r >255] = 255 r[r < 0] = 0 img[:, :, 0] = b img[:, :, 1] = g img[:, :, 2] = r return img def gray_world_assumes_white_balance(img): """ Gray world hypothesis :param img: image data read by cv2.imread :return: The returned white balance result image data """ B, G, R = np.double(img[:, :, 0]), np.double(img[:, :, 1]), np.double(img[:, :, 2]) B_ave, G_ave, R_ave = np.mean(B), np.mean(G), np.mean(R) K = (B_ave + G_ave + R_ave) / 3 Kb, Kg, Kr = K / B_ave, K / G_ave, K / R_ave Ba = (B * Kb) Ga = (G * Kg) Ra = (R * Kr) # for i in range(len(Ba)): # for j in range(len(Ba[0])): # Ba[i][j] = 255 if Ba[i][j] >255 else Ba[i][j] # Ga[i][j] = 255 if Ga[i][j] > 255 else Ga[i][j] # Ra[i][j] = 255 if Ra[i][j] > 255 else Ra[i][j] Ba[Ba > 255] = 255 Ga[Ga > 255] = 255 Ra[Ra > 255] = 255 # print(np.mean(Ba), np.mean(Ga), np.mean(Ra)) dst_img = np.uint8(np.zeros_like(img)) dst_img[:, :, 0] = Ba dst_img[:, :, 1] = Ga dst_img[:, :, 2] = Ra return dst_img def color_correction_of_image_analysis(img): """ Color cast detection and color correction method based on image analysis :param img: image data read by cv2.imread :return: The returned white balance result image data """ def detection(img): """Calculate color cast value""" img_lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB) l, a, b = cv2.split(img_lab) d_a, d_b, M_a, M_b = 0, 0, 0, 0 for i in range(m): for j in range(n): d_a = d_a + a[i][j] d_b = d_b + b[i][j] d_a, d_b = (d_a / (m * n)) - 128, (d_b / (n * m)) - 128 D = np.sqrt((np.square(d_a) + np.square(d_b))) for i in range(m): for j in range(n): M_a = np.abs(a[i][j] - d_a - 128) + M_a M_b = np.abs(b[i][j] - d_b - 128) + M_b M_a, M_b = M_a / (m * n), M_b / (m * n) M = np.sqrt((np.square(M_a) + np.square(M_b))) k = D / M # print('Color cast value:%f'% k) return b, g, r = cv2.split(img) # print(img.shape) m, n = b.shape # detection(img) I_r_2 = np.zeros(r.shape) I_b_2 = np.zeros(b.shape) # sum_I_r_2, sum_I_r, sum_I_b_2, sum_I_b, sum_I_g = 0, 0, 0, 0, 0 # max_I_r_2, max_I_r, max_I_b_2, max_I_b, max_I_g = int(r[0][0] ** 2), int(r[0][0]), int(b[0][0] ** 2), int( # b[0][0]), int(g[0][0]) # # for i in range(m): # for j in range(n): # I_r_2[i][j] = int(r[i][j] ** 2) # I_b_2[i][j] = int(b[i][j] ** 2) # sum_I_r_2 = I_r_2[i][j] + sum_I_r_2 # sum_I_b_2 = I_b_2[i][j] + sum_I_b_2 # sum_I_g = g[i][j] + sum_I_g # sum_I_r = r[i][j] + sum_I_r # sum_I_b = b[i][j] + sum_I_b # if max_I_r < r[i][j]: # max_I_r = r[i][j] # if max_I_r_2 < I_r_2[i][j]: # max_I_r_2 = I_r_2[i][j] # if max_I_g < g[i][j]: # max_I_g = g[i][j] # if max_I_b_2 < I_b_2[i][j]: # max_I_b_2 = I_b_2[i][j] # if max_I_b < b[i][j]: # max_I_b = b[i][j] I_r_2 = (r.astype(np.float32) ** 2).astype(np.float32) I_b_2 = (b.astype(np.float32) ** 2).astype(np.float32) sum_I_r_2 = I_r_2.sum() sum_I_b_2 = I_b_2.sum() sum_I_g = g.sum() sum_I_r = r.sum() sum_I_b = b.sum() max_I_r = r.max() max_I_g = g.max() max_I_b = b.max() max_I_r_2 = I_r_2.max() max_I_b_2 = I_b_2.max() [u_b, v_b] = np.matmul(np.linalg.inv([[sum_I_b_2, sum_I_b], [max_I_b_2, max_I_b]]), [sum_I_g, max_I_g]) [u_r, v_r] = np.matmul(np.linalg.inv([[sum_I_r_2, sum_I_r], [max_I_r_2, max_I_r]]), [sum_I_g, max_I_g]) # print(u_b, v_b, u_r, v_r) # b0, g0, r0 = np.zeros(b.shape, np.uint8), np.zeros(g.shape, np.uint8), np.zeros(r.shape, np.uint8) # for i in range(m): # for j in range(n): # b_point = u_b * (b[i][j] ** 2) + v_b * b[i][j] # g0[i][j] = g[i][j] # # r0[i][j] = r[i][j] # r_point = u_r * (r[i][j] ** 2) + v_r * r[i][j] # if r_point >255: # r0[i][j] = 255 # else: # if r_point < 0: # r0[i][j] = 0 # else: # r0[i][j] = r_point # if b_point >255: # b0[i][j] = 255 # else: # if b_point < 0: # b0[i][j] = 0 # else: # b0[i][j] = b_point b_point = u_b * (b.astype(np.float32) ** 2) + v_b * b.astype(np.float32) r_point = u_r * (r.astype(np.float32) ** 2) + v_r * r.astype(np.float32) b_point[b_point >255] = 255 b_point[b_point < 0] = 0 b = b_point.astype(np.uint8) r_point[r_point >255] = 255 r_point[r_point < 0] = 0 r = r_point.astype(np.uint8) return cv2.merge([b, g, r]) def dynamic_threshold_white_balance(img): """ Dynamic threshold algorithm The algorithm is divided into two steps: white point detection and white point adjustment. It's just that the white point detection is not the same as the perfect reflection algorithm, which considers the brightest point as the white point, but is determined by another rule :param img: image data read by cv2.imread :return: The returned white balance result image data """ b, g, r = cv2.split(img) """ YUV space """ def con_num(x): if x >0: return 1 if x < 0: return -1 if x == 0: return 0 yuv_img = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb) (y, u, v) = cv2.split(yuv_img) # y, u, v = cv2.split(img) m, n = y.shape sum_u, sum_v = 0, 0 max_y = np.max(y.flatten()) # print(max_y) # for i in range(m): # for j in range(n): # sum_u = sum_u + u[i][j] # sum_v = sum_v + v[i][j] sum_u = u.sum() sum_v = v.sum() avl_u = sum_u / (m * n) avl_v = sum_v / (m * n) du, dv = 0, 0 # print(avl_u, avl_v) # for i in range(m): # for j in range(n): # du = du + np.abs(u[i][j] - avl_u) # dv = dv + np.abs(v[i][j] - avl_v) du = (np.abs(u - avl_u)).sum() dv = (np.abs(v - avl_v)).sum() avl_du = du / (m * n) avl_dv = dv / (m * n) num_y, yhistogram, ysum = np.zeros(y.shape), np.zeros(256), 0 radio = 0.5 # If the value is too large or too small, the color temperature will develop to two extremes # for i in range(m): # for j in range(n): # value = 0 # if np.abs(u[i][j] - (avl_u + avl_du * con_num(avl_u))) < radio * avl_du or np.abs( # v[i][j] - (avl_v + avl_dv * con_num(avl_v))) < radio * avl_dv: # value = 1 # else: # value = 0 # # if value 0: yhistogram[int(num_y[i][j])] = 1 + yhistogram[int(num_y[i][j])] ysum = (temp).sum() sum_yhistogram = 0 # hists2, bins = np.histogram(yhistogram, 256, [0, 256]) # print(hists2) Y = 255 num, key = 0, 0 while Y >= 0: num += yhistogram[Y] if num> 0.1 * ysum: # Take the first 10% of the bright spots as the calculated value. If the value is too large, it is easy to overexpose. If the value is too small, the adjustment range is small key = Y break Y = Y - 1 # print(key) sum_r, sum_g, sum_b, num_rgb = 0, 0, 0, 0 # for i in range(m): # for j in range(n): # if num_y[i][j] > key: # sum_r = sum_r + r[i][j] # sum_g = sum_g + g[i][j] # sum_b = sum_b + b[i][j] # num_rgb += 1 num_rgb = (num_y > key).sum() sum_r = r[num_y > key].sum() sum_g = g[num_y > key].sum() sum_b = b[num_y > key].sum() if num_rgb == 0: return img # print("num_rgb", num_rgb) avl_r = sum_r / num_rgb avl_g = sum_g / num_rgb avl_b = sum_b / num_rgb # for i in range(m): # for j in range(n): # b_point = int(b[i][j]) * int(max_y) / avl_b # g_point = int(g[i][j]) * int(max_y) / avl_g # r_point = int(r[i][j]) * int(max_y) / avl_r # if b_point > 255: # b[i][j] = 255 # else: # if b_point < 0: # b[i][j] = 0 # else: # b[i][j] = b_point # if g_point >255: # g[i][j] = 255 # else: # if g_point < 0: # g[i][j] = 0 # else: # g[i][j] = g_point # if r_point >255: # r[i][j] = 255 # else: # if r_point < 0: # r[i][j] = 0 # else: # r[i][j] = r_point b_point = b.astype(np.float32) * int(max_y) / avl_b g_point = g.astype(np.float32) * int(max_y) / avl_g r_point = r.astype(np.float32) * int(max_y) / avl_r b_point[b_point >255] = 255 b_point[b_point < 0] = 0 b = b_point.astype(np.uint8) g_point[g_point >255] = 255 g_point[g_point < 0] = 0 g = g_point.astype(np.uint8) r_point[r_point >255] = 255 r_point[r_point < 0] = 0 r = r_point.astype(np.uint8) return cv2.merge([b, g, r]) def gamma_trans(img): """ gamma correction Use adaptive gamma correction :param img: image data read by cv2.imread :return: the returned image data after gamma correction """ img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) mean = np.mean(img_gray) gamma = math.log10(0.5) / math.log10(mean / 255) # The formula calculates gamma gamma_table = [np.power(x / 255.0, gamma) * 255.0 for x in range(256)] # Create a mapping table gamma_table = np.round(np.array(gamma_table)).astype(np.uint8) # The color value is an integer return cv2.LUT(img, gamma_table) # Look up the table of image colors. In addition, an adaptive algorithm can be designed according to the principle of light intensity (color) uniformity. def contrast_image_correction(img): """ Python reproduction of contrast image correction papers, HDR technology :param img: image data read by cv2.imread :return: the returned image data after HDR correction """ img_yuv = cv2.cvtColor(img, cv2.COLOR_BGR2YUV) mv = cv2.split(img_yuv) img_y = mv[0].copy(); # temp = img_y temp = cv2.bilateralFilter(mv[0], 9, 50, 50) # for i in range(len(img)): # for j in range(len(img[0])): # exp = np.power(2, (128 - (255 - temp[i][j])) / 128.0) # temp[i][j] = int(255 * np.power(img_y[i][j] / 255.0, exp)) # # print(exp.dtype) # print(temp.dtype) exp = np.power(2, (128.0 - (255 - temp).astype(np.float32)) / 128.0) temp = (255 * np.power(img_y.flatten() / 255.0, exp.flatten())).astype(np.uint8) temp = temp.reshape((img_y.shape)) dst = img.copy() img_y[img_y == 0] = 1 for k in range(3): val = temp / img_y val1 = img[:, :, k].astype(np.int32) + img_y.astype(np.int32) val2 = (val * val1 + img[:, :, k] - img_y) / 2 dst[:, :, k] = val2.astype(np.int32) # for i in range(len(img)): # for j in range(len(img[0])): # if (img_y[i][j] == 0): # dst[i, j, :] = 0 # else: # for k in range(3): # val = temp[i, j]/img_y[i, j] # val1 = int(img[i, j, k]) + int(img_y[i, j]) # val2 = (val * val1+ img[i, j, k] - img_y[i, j]) / 2 # dst[i, j, k] = int(val2) # """ # BUG: Directly using the following calculation method will cause the value to overflow, resulting in incorrect calculation results # """ # dst[i, j, 0] = (temp[i, j] * (img[i, j, 0] + img_y[i, j]) / img_y[i, j] + img[i, j, 0] - img_y[ # i, j]) / 2 # dst[i, j, 1] = (temp[i, j] * (img[i, j, 1] + img_y[i, j]) / img_y[i, j] + img[i, j, 1] - img_y[ # i, j]) / 2 # dst[i, j, 2] = (temp[i, j] * (img[i, j, 2] + img_y[i, j]) / img_y[i, j] + img[i, j, 2] - img_y[ # i, j]) / 2 return dst if __name__ == '__main__': """ img: original image img1: Mean white balance method img2: perfect reflection img3: Grayscale world hypothesis img4: Color cast detection and color correction method based on image analysis img5: Dynamic threshold algorithm img6: gamma correction img7: HDR correction """ import time img = cv2.imread("4.jpg") start_time = time.time() # img = cv2.resize(img, (256, 512), cv2.INTER_LINEAR) # img1 = mean_white_balance(img) # img2 = perfect_reflective_white_balance(img) # img3 = gray_world_assumes_white_balance(img) # img4 = color_correction_of_image_analysis(img) # img5 = dynamic_threshold_white_balance(img) # img6 = gamma_trans(img) # gamma transformation # img7 = contrast_image_correction(img) img8 = flip_img(img) end_time = time.time() print("use time=", end_time - start_time) cv2.imshow("image1", img) cv2.imshow("image2", img8) # img_stack = np.vstack([img, img1, img2, img3]) # img_stack2 = np.vstack([img4, img5, img6, img7]) # cv2.imshow("image1",img_stack) # cv2.imshow("image2",img_stack2) cv2.waitKey(0)
references:
The principle of gray world, perfect reflection, dynamic threshold and its C++ implementation:
HDR correction: contrast image correction
How to enable/disable manual white balance in OpenCV (Python)
Using OpenCV on Linux, if you have a video device that interfaces a V4L2 device such as a USB webcam:
you can typically enable automatic white balance (= disable manual white balance) for any camera by using
camera.set(cv2.CAP_PROP_AUTO_WB, 1.0) # Enable automatic white balance
or disable automatic white balance (= enable manual white balance) using
camera.set(cv2.CAP_PROP_AUTO_WB, 0.0) # Disable automatic white balance
When disabling automatic white balance, you should also set the manual white balance temperature – see our post How to set manual white balance temperature in OpenCV (Python) for more details.
For V4L2 cameras, as you can see in our previous post on mapping of OpenCV parameters to V4L2 parameters, CAP_PROP_AUTO_WB is mapped to V4L2_CID_AUTO_WHITE_BALANCE which is shown in v4l2-ctl -d /dev/video0 --all as white_balance_temperature_auto . Therefore, you can easily verify if, for example, disabling the auto white balance worked for your V4L2 camera such as any USB camera by looking at the white_balance_temperature_auto section of v4l2-ctl -d /dev/video0 --all :
white_balance_temperature_auto 0x0098090c (bool) : default=1 value=0
If this post helped you, please consider buying me a coffee or donating via PayPal to support research & publishing of new posts on TechOverflow