Python 使用OpenCV进行网络摄像头颜色校准

Python 使用OpenCV进行网络摄像头颜色校准,python,opencv,image-processing,computer-vision,webcam,Python,Opencv,Image Processing,Computer Vision,Webcam,在为一个项目在线订购了半打网络摄像头后,我注意到输出上的颜色不一致 为了补偿这一点,我尝试拍摄一幅模板图像,提取R、G和B直方图,并尝试基于此匹配目标图像的RGB直方图 这是从一个非常类似的问题的解决方案的描述中得到启发的 完美的解决方案如下所示: 为了解决这个问题,我编写了以下脚本,但效果很差: 编辑(感谢@DanMašek和@api55) 模板图像: 目标图像: 匹配的图像: 然后我在下面的链接中发现Adrian(pyimagesearch)试图解决一个非常类似的问题 结果似乎是相

在为一个项目在线订购了半打网络摄像头后,我注意到输出上的颜色不一致

为了补偿这一点,我尝试拍摄一幅模板图像,提取R、G和B直方图,并尝试基于此匹配目标图像的RGB直方图

这是从一个非常类似的问题的解决方案的描述中得到启发的

完美的解决方案如下所示:

为了解决这个问题,我编写了以下脚本,但效果很差:

编辑(感谢@DanMašek和@api55) 模板图像:

目标图像:

匹配的图像:

然后我在下面的链接中发现Adrian(pyimagesearch)试图解决一个非常类似的问题


结果似乎是相当好的一些饱和缺陷。我欢迎任何关于如何解决此问题的建议或指针,以便所有web cam输出都可以根据一个模板图像进行校准,以输出类似的颜色。

您的脚本性能不佳,因为您使用了错误的索引

OpenCV图像是BGR,因此这在代码中是正确的:

source = cv2.imread('/media/somadetect/Lexar/color_transfer_data/1/frame10.png')
s_b = source[:,:,0]
s_g = source[:,:,1]
s_r = source[:,:,2]
template =  cv2.imread('/media/somadetect/Lexar/color_transfer_data/5/frame6.png')
t_b = source[:,:,0]
t_r = source[:,:,1]
t_g = source[:,:,2]
但这是错误的

transfer[:,:,0] = matched_r
transfer[:,:,1] = matched_g
transfer[:,:,2] = matched_b
因为这里您使用的是RGB而不是BGR,所以颜色会发生变化,您的OpenCV仍然认为它是BGR。这就是为什么它看起来很奇怪

应该是:

transfer[:,:,0] = matched_b
transfer[:,:,1] = matched_g
transfer[:,:,2] = matched_r
作为其他可能的解决方案,您可以尝试查看可以在相机中设置哪些参数。有时,它们具有一些自动参数,您可以手动设置这些参数以使所有参数匹配。此外,请注意此自动参数,通常白平衡和焦距以及其他参数设置为自动,并且在同一相机中,它们可能会在不同时间发生很大变化(取决于照明等)

更新: 正如DanMašek所指出的那样

t_b = source[:,:,0]
t_r = source[:,:,1]
t_g = source[:,:,2]
是错误的,因为r应该是索引2,g应该是索引1

t_b = source[:,:,0]
t_g = source[:,:,1]
t_r = source[:,:,2]

我尝试了一个基于白色补丁的校准例程。这里是链接

代码片段如下所示:

import cv2
import math
import numpy as np
import sys
from matplotlib import pyplot as plt

def hist_match(source, template):
    """
    Adjust the pixel values of a grayscale image such that its histogram
    matches that of a target image

    Arguments:
    -----------
        source: np.ndarray
            Image to transform; the histogram is computed over the flattened
            array
        template: np.ndarray
            Template image; can have different dimensions to source
    Returns:
    -----------
        matched: np.ndarray
            The transformed output image
    """

    oldshape = source.shape
    source = source.ravel()
    template = template.ravel()

    # get the set of unique pixel values and their corresponding indices and
    # counts
    s_values, bin_idx, s_counts = np.unique(source, return_inverse=True,
                                            return_counts=True)
    t_values, t_counts = np.unique(template, return_counts=True)

    # take the cumsum of the counts and normalize by the number of pixels to
    # get the empirical cumulative distribution functions for the source and
    # template images (maps pixel value --> quantile)
    s_quantiles = np.cumsum(s_counts).astype(np.float64)
    s_quantiles /= s_quantiles[-1]
    t_quantiles = np.cumsum(t_counts).astype(np.float64)
    t_quantiles /= t_quantiles[-1]

    # interpolate linearly to find the pixel values in the template image
    # that correspond most closely to the quantiles in the source image
    interp_t_values = np.interp(s_quantiles, t_quantiles, t_values)
    return interp_t_values[bin_idx].reshape(oldshape)

# Read original image
im_o = cv2.imread('/media/Lexar/color_transfer_data/5/frame10.png')
im = im_o
cv2.imshow('Org',im)
cv2.waitKey()

B = im[:,:, 0]
G = im[:,:, 1]
R = im[:,:, 2]

R= np.array(R).astype('float')
G= np.array(G).astype('float')
B= np.array(B).astype('float')

# Extract pixels that correspond to pure white R = 255,G = 255,B = 255
B_white = R[168, 351]
G_white = G[168, 351]
R_white = B[168, 351]

print B_white
print G_white
print R_white

# Compensate for the bias using normalization statistics
R_balanced = R / R_white
G_balanced = G / G_white
B_balanced = B / B_white

R_balanced[np.where(R_balanced > 1)] = 1
G_balanced[np.where(G_balanced > 1)] = 1
B_balanced[np.where(B_balanced > 1)] = 1

B_balanced=B_balanced * 255
G_balanced=G_balanced * 255
R_balanced=R_balanced * 255

B_balanced= np.array(B_balanced).astype('uint8')
G_balanced= np.array(G_balanced).astype('uint8')
R_balanced= np.array(R_balanced).astype('uint8')

im[:,:, 0] = (B_balanced)
im[:,:, 1] = (G_balanced)
im[:,:, 2] = (R_balanced)

# Notice saturation artifacts 
cv2.imshow('frame',im)
cv2.waitKey()

# Extract the Y plane in original image and match it to the transformed image 
im_o = cv2.cvtColor(im_o, cv2.COLOR_BGR2YCR_CB)
im_o_Y = im_o[:,:,0]

im = cv2.cvtColor(im, cv2.COLOR_BGR2YCR_CB)
im_Y = im[:,:,0]

matched_y = hist_match(im_o_Y, im_Y)
matched_y= np.array(matched_y).astype('uint8')
im[:,:,0] = matched_y

im_final = cv2.cvtColor(im, cv2.COLOR_YCR_CB2BGR)
cv2.imshow('frame',im_final)
cv2.waitKey()
输入图像为:

脚本的结果是:


谢谢大家的建议和指点

虽然我对机器视觉知之甚少,但这种颜色转移方法对我来说似乎并不合适。这是一个完全不同的任务,它正在接近。在你的情况下,我想,你想要的东西有一些基本的模型,这解释了为什么那些相机的行为是不同的!忽略这样一个模型可能会导致自由度过多/正则化不够,从而导致糟糕的结果。同样,没有太多的经验,但这些相机内部肯定不是与RGB,但可能是一些拜耳模式,它允许一个更调谐的噪声模型(传感器数据可用?)。你订购了十几个不同品牌的相机,或者它们在同一品牌中是不同的?我觉得即使是第一部分也是不正确的——注意
t\u r
t\u g
是如何交换的。@DanMašek True,我没有注意到,怪不得它不起作用。。。所有的频道都感谢你指出我在指数@api55上的草率错误,我当然需要更多的咖啡因。然而,基于模板的颜色校准似乎还不能解决我的问题。@BharathS至少算法似乎做了它应该做的事情:)目标看起来几乎像matched@api55对。我试图匹配目标图像,使其与模板相似。然而,我正在接近一个解决方案,将很快更新这篇文章。
import cv2
import math
import numpy as np
import sys
from matplotlib import pyplot as plt

def hist_match(source, template):
    """
    Adjust the pixel values of a grayscale image such that its histogram
    matches that of a target image

    Arguments:
    -----------
        source: np.ndarray
            Image to transform; the histogram is computed over the flattened
            array
        template: np.ndarray
            Template image; can have different dimensions to source
    Returns:
    -----------
        matched: np.ndarray
            The transformed output image
    """

    oldshape = source.shape
    source = source.ravel()
    template = template.ravel()

    # get the set of unique pixel values and their corresponding indices and
    # counts
    s_values, bin_idx, s_counts = np.unique(source, return_inverse=True,
                                            return_counts=True)
    t_values, t_counts = np.unique(template, return_counts=True)

    # take the cumsum of the counts and normalize by the number of pixels to
    # get the empirical cumulative distribution functions for the source and
    # template images (maps pixel value --> quantile)
    s_quantiles = np.cumsum(s_counts).astype(np.float64)
    s_quantiles /= s_quantiles[-1]
    t_quantiles = np.cumsum(t_counts).astype(np.float64)
    t_quantiles /= t_quantiles[-1]

    # interpolate linearly to find the pixel values in the template image
    # that correspond most closely to the quantiles in the source image
    interp_t_values = np.interp(s_quantiles, t_quantiles, t_values)
    return interp_t_values[bin_idx].reshape(oldshape)

# Read original image
im_o = cv2.imread('/media/Lexar/color_transfer_data/5/frame10.png')
im = im_o
cv2.imshow('Org',im)
cv2.waitKey()

B = im[:,:, 0]
G = im[:,:, 1]
R = im[:,:, 2]

R= np.array(R).astype('float')
G= np.array(G).astype('float')
B= np.array(B).astype('float')

# Extract pixels that correspond to pure white R = 255,G = 255,B = 255
B_white = R[168, 351]
G_white = G[168, 351]
R_white = B[168, 351]

print B_white
print G_white
print R_white

# Compensate for the bias using normalization statistics
R_balanced = R / R_white
G_balanced = G / G_white
B_balanced = B / B_white

R_balanced[np.where(R_balanced > 1)] = 1
G_balanced[np.where(G_balanced > 1)] = 1
B_balanced[np.where(B_balanced > 1)] = 1

B_balanced=B_balanced * 255
G_balanced=G_balanced * 255
R_balanced=R_balanced * 255

B_balanced= np.array(B_balanced).astype('uint8')
G_balanced= np.array(G_balanced).astype('uint8')
R_balanced= np.array(R_balanced).astype('uint8')

im[:,:, 0] = (B_balanced)
im[:,:, 1] = (G_balanced)
im[:,:, 2] = (R_balanced)

# Notice saturation artifacts 
cv2.imshow('frame',im)
cv2.waitKey()

# Extract the Y plane in original image and match it to the transformed image 
im_o = cv2.cvtColor(im_o, cv2.COLOR_BGR2YCR_CB)
im_o_Y = im_o[:,:,0]

im = cv2.cvtColor(im, cv2.COLOR_BGR2YCR_CB)
im_Y = im[:,:,0]

matched_y = hist_match(im_o_Y, im_Y)
matched_y= np.array(matched_y).astype('uint8')
im[:,:,0] = matched_y

im_final = cv2.cvtColor(im, cv2.COLOR_YCR_CB2BGR)
cv2.imshow('frame',im_final)
cv2.waitKey()