python-opencv五种自动白平衡算法,附源码直接可用(均值、完美反射、灰度世界、动态阈值、基于图像分析的偏色检测及颜色校正)
opencv
OpenCV: 开源计算机视觉库
项目地址:https://gitcode.com/gh_mirrors/opencv31/opencv
免费下载资源
·
最近研究了自动白平衡的几种方法,参考了不少,最为感谢python opencv白平衡算法(但是这篇文章提供的算法没有考虑到uint8格式问题,产生了图像的局部失真,这里做了改进):(<-原图,失真图->)
谈谈总体理解:
(本来目标是同一张图,无论在什么样子的滤镜、光照下最后白平衡结果要尽可能相同,最后发现都太难了)
1.均值、灰度世界都建立一种计算平均的算法基础上,适用于色彩分布比较全面平均的场景,其实在很多场合都不适用
2.完美反射、动态阈值建立在白点的基础上,比如完美反射认为最亮的点为白点,以白点为基础进行整体的调节,导致的问题在于如果整张图没有白点算法效果非常不好,其次,由于不同色温下白点所呈现的数值差异性很大,导致白平衡结果不尽如人意。且Ratio的选取也有效果差异。还有一种做法是固定某一区域为白色区域然后算法计算,延展全图,效果展示使用uint8格式时一定要注意的问题(python-opencv完美反射白平衡算法)
3.基于图像分析的偏色检测及颜色校正,看了这篇原文,感觉整体意思是提供一种偏色检测的做法,然后还是采用基于完美反射、灰度世界的改进算法进行白平衡,效果同样局限。
结果展示,在不同的场景下每种白平衡结果都有不同,没有通用性的最好算法:
第一张: 原图
第二张:均值白平衡法
第三张: 完美反射
第四张: 灰度世界假设
第五张: 基于图像分析的偏色检测及颜色校正方法
第六张: 动态阈值算法
源码:
import cv2
import numpy as np
import random
def white_balance_1(img):
'''
第一种简单的求均值白平衡法
:param img: cv2.imread读取的图片数据
:return: 返回的白平衡结果图片数据
'''
# 读取图像
r, g, b = cv2.split(img)
r_avg = cv2.mean(r)[0]
g_avg = cv2.mean(g)[0]
b_avg = cv2.mean(b)[0]
# 求各个通道所占增益
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 white_balance_2(img_input):
'''
完美反射白平衡
STEP 1:计算每个像素的R\G\B之和
STEP 2:按R+G+B值的大小计算出其前Ratio%的值作为参考点的的阈值T
STEP 3:对图像中的每个点,计算其中R+G+B值大于T的所有点的R\G\B分量的累积和的平均值
STEP 4:对每个点将像素量化到[0,255]之间
依赖ratio值选取而且对亮度最大区域不是白色的图像效果不佳。
:param img: cv2.imread读取的图片数据
:return: 返回的白平衡结果图片数据
'''
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])
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
time = 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
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
return img
def white_balance_3(img):
'''
灰度世界假设
:param img: cv2.imread读取的图片数据
:return: 返回的白平衡结果图片数据
'''
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]
# 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 white_balance_4(img):
'''
基于图像分析的偏色检测及颜色校正方法
:param img: cv2.imread读取的图片数据
:return: 返回的白平衡结果图片数据
'''
def detection(img):
'''计算偏色值'''
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('偏色值:%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]
[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
return cv2.merge([b0, g0, r0])
def white_balance_5(img):
'''
动态阈值算法
算法分为两个步骤:白点检测和白点调整。
只是白点检测不是与完美反射算法相同的认为最亮的点为白点,而是通过另外的规则确定
:param img: cv2.imread读取的图片数据
:return: 返回的白平衡结果图片数据
'''
b, g, r = cv2.split(img)
"""
YUV空间
"""
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]
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)
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 # 如果该值过大过小,色温向两极端发展
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:
continue
num_y[i][j] = y[i][j]
yhistogram[int(num_y[i][j])] = 1 + yhistogram[int(num_y[i][j])]
ysum += 1
# print(yhistogram.shape)
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: # 取前10%的亮点为计算值,如果该值过大易过曝光,该值过小调整幅度小
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
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
return cv2.merge([b, g, r])
'''
img : 原图
img1:均值白平衡法
img2: 完美反射
img3: 灰度世界假设
img4: 基于图像分析的偏色检测及颜色校正方法
img5: 动态阈值算法
'''
img = cv2.imread('./dataset/1/3.JPG')
# img = cv2.imread('./dataset/2/1_'+str(i)+'.JPG')
img1 = white_balance_1(img)
img2 = white_balance_2(img)
img3 = white_balance_3(img)
img4 = white_balance_4(img)
img5 = white_balance_5(img)
print('----------------------')
img_stack = np.vstack([img,img1,img2,img3,img4,img5])
# cv2.imwrite("./dataset/"+str(i)+'.JPG',img_stack)
cv2.imshow('image',img_stack)
cv2.waitKey(0)
GitHub 加速计划 / opencv31 / opencv
142
15
下载
OpenCV: 开源计算机视觉库
最近提交(Master分支:3 个月前 )
d9a139f9
Animated WebP Support #25608
related issues #24855 #22569
### Pull Request Readiness Checklist
See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request
- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake
1 天前
09030615
V4l default image size #25500
Added ability to set default image width and height for V4L capture. This is required for cameras that does not support 640x480 resolution because otherwise V4L capture cannot be opened and failed with "Pixel format of incoming image is unsupported by OpenCV" and then with "can't open camera by index" message. Because of the videoio architecture it is not possible to insert actions between CvCaptureCAM_V4L::CvCaptureCAM_V4L and CvCaptureCAM_V4L::open so the only way I found is to use environment variables to preselect the resolution.
Related bug report is [#25499](https://github.com/opencv/opencv/issues/25499)
Maybe (but not confirmed) this is also related to [#24551](https://github.com/opencv/opencv/issues/24551)
This fix was made and verified in my local environment: capture board AVMATRIX VC42, Ubuntu 20, NVidia Jetson Orin.
### Pull Request Readiness Checklist
See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request
- [X] I agree to contribute to the project under Apache 2 License.
- [X] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [X] The PR is proposed to the proper branch
- [X] There is a reference to the original bug report and related work
- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake
1 天前
更多推荐
已为社区贡献5条内容
所有评论(0)