2017-08-28 9 views
1

Après avoir commandé une demi-douzaine de webcams en ligne pour un projet, je remarque que les couleurs de la sortie ne sont pas cohérentes. Afin de compenser cela, j'ai essayé de prendre une image modèle et d'extraire les histogrammes R, G et B et essayé de faire correspondre les histogrammes RVB des images cibles en fonction de cela.Calibrage de la couleur de la webcam en utilisant OpenCV

Cela a été inspiré de la description de la solution pour un problème très similaire Comparative color calibration

La solution parfaite ressemblera à ceci:

enter image description here

Pour tenter de résoudre ce que j'ai écrit la script suivant qui a mal performé:

EDIT (Merci à @ DanMašek et @ 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) 

image Modèle:

enter image description here

image cible:

enter image description here

L'image matched:

enter image description here

Ensuite, j'ai trouvé la tentative d'Adrian (de pyimagesearch) pour résoudre un problème similaire dans le lien suivant

Fast Color Transfer

Les résultats semblent être assez bonne avec quelques défauts de saturation. J'apprécierais toute suggestion ou indication sur la façon de résoudre ce problème afin que toutes les sorties de la webcam puissent être calibrées pour produire des couleurs similaires basées sur une image modèle.

+0

Même si je ne connais pas grand-chose à la vision industrielle, cette approche de transfert de couleur ne me semble pas juste. C'est une tâche complètement différente. Dans votre cas, je suppose, vous voulez quelque chose qui a un modèle sous-jacent, ce qui explique pourquoi ces caméras se comportent différemment! Ignorer un tel modèle pourrait conduire à trop de degrés de liberté/pas assez de régularisation et donc de mauvais résultats. Encore une fois, pas beaucoup d'expérience, mais ces caméras en interne ne fonctionnent sûrement pas avec RVB, mais probablement un modèle Bayer qui permet un modèle de bruit plus ajusté * (capteur-données disponibles?). – sascha

+0

Avez-vous commandé une douzaine de caméras de marques différentes ou sont-elles différentes au sein d'une même marque? – Arturo

Répondre

1

J'ai essayé une routine d'étalonnage à base de patchs blancs. Voici le lien https://theiszm.wordpress.com/tag/white-balance/.

L'extrait de code suivant:

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

L'image d'entrée est:

enter image description here

Le résultat du script est:

enter image description here

Merci à tous pour suggestions et pointeurs! !

1

Votre script fonctionne mal parce que vous utilisez un mauvais index.

images OpenCV sont BGR, donc c'était correct dans votre code:

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] 

mais cela est faux

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

puisqu'ici vous utilisez RGB et non BGR, de sorte que les changements de couleur et votre OpenCV pense toujours que c'est BGR. C'est pourquoi ça a l'air bizarre.

Il devrait être:

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

Comme d'autres solutions possibles, vous pouvez essayer de regarder quels paramètres peuvent être définis dans votre appareil photo. Parfois, ils ont des paramètres automatiques que vous pouvez définir manuellement pour qu'ils correspondent tous. Aussi, méfiez-vous de ces paramètres automatiques, généralement la balance des blancs et la mise au point et d'autres sont réglés automatiquement et ils peuvent changer beaucoup dans le même appareil photo d'une fois à l'autre (selon l'éclairage, etc etc).

MISE À JOUR:

Comme DanMašek souligne, également

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

est faux, puisque la r devrait être l'indice 2 et g indice 1

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

IMHO même la première partie n'est pas correcte - notez comment 't_r' et' t_g' sont échangés. –

+1

@ DanMašek Vrai, je ne l'ai pas remarqué, pas étonnant que ça ne marche pas ... tous les canaux où on a mélangé – api55

+0

Merci d'avoir signalé mon erreur bâclée avec indices @ api55 J'ai certainement besoin de plus de caféine. Cependant, il ne semble pas encore résoudre mon problème avec l'étalonnage des couleurs basé sur un modèle. –