2017-08-28 128 views
1

在爲一個項目訂購了半打網絡攝像頭之後,我注意到輸出上的顏色不一致。使用OpenCV進行網絡攝像頭色彩校準

爲了彌補這一點,我試圖採取模板圖像並提取R,G和B直方圖,並嘗試在此基礎上匹配目標圖像的RGB直方圖。

這從溶液中的描述激發了一個非常類似的問題Comparative color calibration

完美的解決方案將是這樣的:

enter image description here

爲了嘗試解決這個我寫的以下腳本表現不佳:

編輯(感謝@DanMašek和@ api55)

import numpy as np 

def show_image(title, image, width = 300): 
    # resize the image to have a constant width, just to 
    # make displaying the images take up less screen real 
    # estate 
    r = width/float(image.shape[1]) 
    dim = (width, int(image.shape[0] * r)) 
    resized = cv2.resize(image, dim, interpolation = cv2.INTER_AREA) 

    # show the resized image 
    cv2.imshow(title, resized) 


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) 

from matplotlib import pyplot as plt 
from scipy.misc import lena, ascent 
import cv2 

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] 

matched_b = hist_match(s_b, t_b) 
matched_g = hist_match(s_g, t_g) 
matched_r = hist_match(s_r, t_r) 

y,x,c = source.shape 
transfer = np.empty((y,x,c), dtype=np.uint8) 

transfer[:,:,0] = matched_r 
transfer[:,:,1] = matched_g 
transfer[:,:,2] = matched_b 

show_image("Template", template) 
show_image("Target", source) 
show_image("Transfer", transfer) 
cv2.waitKey(0) 

模板圖像:

enter image description here

目標圖像:

enter image description here

匹配圖像:

enter image description here

然後我發現阿德里安(pyimagesearch)試圖解決一個非常類似的問題在以下鏈接

Fast Color Transfer

結果似乎與一些飽和缺陷還算不錯。我歡迎任何有關如何解決此問題的建議或指示,以便可以校準所有網絡攝像頭輸出以基於一個模板圖像輸出相似的顏色。

+0

雖然對機器視覺知之甚少,但這種顏色轉移方法對我來說似乎不太合適。這是一個完全不同的任務,它正在接近。在你的情況下,我想,你需要一些具有一些基礎模型的東西,這就解釋了爲什麼這些相機的行爲不同!忽視這種模式可能會導致太多的自由度/沒有足夠的正規化程度,從而導致不好的結果。再一次,沒有太多的經驗,但這些相機內部肯定沒有與RGB工作,但可能是一些拜耳模式,它允許更多的調整*噪聲模型*(傳感器數據可用?)。 – sascha

+0

您是否訂購過一打不同品牌的相機,或者它們在同一品牌下有所不同? – Arturo

回答

1

我已嘗試一個基於白色補丁的校準程序。這裏是鏈接https://theiszm.wordpress.com/tag/white-balance/

的代碼片段如下:

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() 

輸入圖像是:

enter image description here

腳本的結果是:

enter image description here

謝謝大家建議和指針! !

1

由於您使用了錯誤的索引,您的腳本執行效果不佳。

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] 

但因爲在這裏使用的是RGB,而不是BGR是錯誤的

transfer[:,:,0] = matched_r 
transfer[:,:,1] = matched_g 
transfer[:,:,2] = matched_b 

,所以顏色變化和你的OpenCV仍然認爲它是BGR。這就是爲什麼它看起來很奇怪。

它應該是:

transfer[:,:,0] = matched_b 
transfer[:,:,1] = matched_g 
transfer[:,:,2] = matched_r 

至於其他可能的解決方案,您可以嘗試一下它的參數可以在你的相機設置。有時他們有一些自動參數,您可以手動設置它們以匹配所有參數。另外,要注意這個自動參數,通常是白平衡和對焦,其他都設置爲自動,並且它們可能會在同一個相機上從一個時間到另一個時間(取決於照明等)發生相當大的變化。

UPDATE:

作爲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] 
+1

恕我直言,即使第一部分是不正確的 - 注意't_r'和't_g'是如何交換的。 –

+1

@DanMašek真的,我沒有注意到它,難怪它不工作...混合所有的渠道 – api55

+0

謝謝你用指數@ api55指出我的馬虎的錯誤我當然需要更多的咖啡因。但它似乎並沒有解決我的問題,基於模板的顏色校準呢。 –