起因:基于上一篇的颜色校正算法基本没什么问题,一部分情况下也能很好的工做,但是有相当一部分的图片,校正后的图像出现严重的色块,甚至出现跟’鬼’一般的效果,如图:(我都不用打码了),上面一张几乎全白了,下面一张也是各种色块
分析:
- 色块肯定是因为校正矩阵把一定范围内的值都映射到同一个值了;
- 颜色与原来的颜色偏色严重肯定是因为校正矩阵的泛化能力太弱,或者说严重过拟合;其实,仔细想想也对,上一篇文章里只是取了色卡上每个色块里的中间邻域的一个均值作为输入,所以泛化能力肯定很弱才对。
验证:
基于上篇的方法,取与标准值偏差很大的颜色作为输入(比如input[2,0] = (14,14,14),与之对应的标准值为(52,52,52)),计算系数矩阵A,然后取(18,18,18)作为输入,得到结果为(127,37,26),这个结果是根本无法接受。所以,矩阵A的计算有问题。这个问题就是病态方程组的问题了,关于病态方程组的理论和解决办法,网上有N多文章,我就不赘述了,有兴趣的可以去看看,而且这个病态矩阵问题还可以帮助理解L1、L2为什么可以防止神经网络过拟合。请参考:L0、L1与L2范数、核范数
解决办法:
从根本解决问题,引起这个问题的根本原因就是拟合的数据太少,导致求解出的系数矩阵严重过拟合,所以增加样本数据,然后不再求解方程组的解析解,而是改用迭代求近似解。核心代码:
from sklearn.preprocessing import PolynomialFeatures
from sklearn import linear_model
from sklearn.linear_model import SGDClassifier
from color_detect import *
from tools import *
import os
ROOT_DIR = r'/home/bqh/code/collor_recorrect'
std_color_file = os.path.join(ROOT_DIR, 'my_color_value.csv')
input_color_file = os.path.join(ROOT_DIR, r'test\1_card.jpg')
input_img_file = os.path.join(ROOT_DIR, r'test\1.jpg')
output_img_file = os.path.join(ROOT_DIR, r'test\1_corrected_card9.jpg')
def create_inputData(image_data):
data = []
for raw_data in image_data:
for bgr in raw_data:
data.append([bgr[2], bgr[1], bgr[0]])
data = np.array(data)
return data
def get_stdColor_value(use_Lab=False):
color_dict = {}
std_matrix = []
color_value_list = np.loadtxt(std_color_file, dtype=np.str, delimiter=',')
if use_Lab:
# 转换到Lab空间
for element in color_value_list:
bgr = (int(element[4]), int(element[3]), int(element[2]))
Lab_value = BGR2Lab(bgr)
color_dict[element[1]] = Lab_value
std_matrix.append([Lab_value[0], Lab_value[1], Lab_value[2]])
else:
for element in color_value_list:
color_dict[element[1]] = (int(element[2]), int(element[3]), int(element[4]))
std_matrix.append([int(element[2]), int(element[3]), int(element[4])])
std_matrix = np.array(std_matrix)
return std_matrix
def recorrect_color(raw_img, poly_reg, lin_reg, out_path):
"""
用系数矩阵A对图像进行颜色校正
"""
w = raw_img.shape[0]
h = raw_img.shape[1]
input_data = create_inputData(raw_img)
data = poly_reg.fit_transform(input_data)
corrected_data = lin_reg.predict(data)
data = np.uint8(np.clip(corrected_data, 0, 255))
data = np.array(data)
new_img = data.reshape((w, h, 3))
new_img = new_img[..., [2, 1, 0]]
# 做一次色阶调整
new_img = AutoBrightness(new_img)
cv2.imwrite(out_path, new_img)
return new_img
def color_recorrect():
# 载入标准色卡数据
std_matrix = get_stdColor_value()
# 载入测试色卡图像,生成回归输入数据
img = cv2.imread(input_color_file, 1)
card_imgs, color_img = img_split(img)
# 构造训练数据和标签
input_data = []
std_data = []
for index, img in enumerate(card_imgs):
h, w = img.shape[0], img.shape[1]
h = h//2
w = w//2
data = create_inputData(img[h - 5: h + 5, w - 5 : w + 5])
input_data.append(data)
std_data.append([std_matrix[index]] * len(data))
input_data = np.array(input_data)
input_data = np.resize(input_data, (900, 3))
std_data = np.array(std_data)
std_data = np.resize(std_data, (900, 3))
poly_reg = PolynomialFeatures(degree=1)
X_ploy = poly_reg.fit_transform(input_data)
lin_reg = linear_model.LinearRegression()
lin_reg.fit(X_ploy, std_data)
# 颜色校正
test_img = cv2.imread(input_img_file)
recorrect_color(test_img,poly_reg, lin_reg, output_img_file)