title: Webcam color calibration using OpenCV
body: <p>Having ordered half a dozen webcams online for a project I notice that the colors on the output are not consistent. </p>
  
  <p>In order to compensate for this I have attempted to take a template image and extract the R,G and B histograms and tried to match the target images's RGB histograms based on this. </p>
  
  <p>This was inspired from the description of the solution for a very similar problem <a href="http://www.matrox.com/imaging/en/products/software/mil/tools/color/" rel="nofollow noreferrer">Comparative color calibration</a></p>
  
  <p>The perfect solution will look like this : </p>
  
  <p><a href="https://i.stack.imgur.com/0Vp7M.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/0Vp7M.jpg" alt="enter image description here"></a></p>
  
  <p>In order to try to solve this I wrote the following script which performed poorly:</p>
  
  <h1>EDIT (Thanks to @DanMašek and @api55)</h1>
  
  <pre><code>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 --&gt; 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)
  </code></pre>
  
  <p>Template image : </p>
  
  <p><a href="https://i.stack.imgur.com/wrp3Y.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/wrp3Y.jpg" alt="enter image description here"></a></p>
  
  <p>Target Image:</p>
  
  <p><a href="https://i.stack.imgur.com/EHU29.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/EHU29.jpg" alt="enter image description here"></a></p>
  
  <p>The Matched Image:</p>
  
  <p><a href="https://i.stack.imgur.com/1SywH.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/1SywH.png" alt="enter image description here"></a></p>
  
  <p>Then I found Adrian's (pyimagesearch) attempt to solve a very similar problem in the following link </p>
  
  <p><a href="https://github.com/jrosebr1/color_transfer" rel="nofollow noreferrer">Fast Color Transfer </a></p>
  
  <p>The results seem to be fairly good with some saturation defects. I would welcome any suggestions or pointers on how to address this issue so all web cam outputs could be calibrated to output similar colors based on one template image.</p>