技术标签: 动态阈值 python # OpenCVPython 自动白平衡 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)
文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr
文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc
文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8
文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束
文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求
文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname
文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include<stdio.h>#include<string.h>#include<stdlib.h>#include<malloc.h>#include<iostream>#include<stack>#include<queue>using namespace std;typed_二叉树的建立
文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码
文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词
文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限
文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定
文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland