title: Precision Measurement with Opencv python
body: <p>I am actually working on a Machine Vision project using OpenCV and Python.</p>
  
  <p><strong>Objective</strong> : The objective of the project is to measure the dimensions of a component with high accuracy.</p>
  
  <p><strong>Main Hardware</strong> :</p>
  
  <ul>
  <li><p>Basler 5MP camera (aca-2500-14gm)</p></li>
  <li><p>A red backlight (100 mm x 100 mm) (Size of my component is around 60mm)</p></li>
  </ul>
  
  <p><strong>Experiment</strong> </p>
  
  <p>Since I am Looking at very tight tolerance limits, I first did a precision study. I kept the component on the backlight source and took 100 images without moving the part (imagine like a video with 100 frames). I measured the Outer Diameter(OD) of all the 100 images. My mm/pixel ratio is <strong>0.042</strong>. I measured the standard deviation of the measurement to find out the precision, which turned out to be around <strong>0.03 mm which is bad</strong>. The component nor the setup is touched thus I was expecting a precision of 0.005 mm. But I am off by an order of magnitude. I am using OpenCV's Hough circle to calculate the OD of the component.</p>
  
  <p>Code:</p>
  
  <pre><code>import sys
  import pickle
  import cv2
  import matplotlib.pyplot as plt
  import glob
  import os
  import numpy as np
  import pandas as pd
  
  def find_circles(image,dp=1.7,minDist=100,param1=50,param2=50,minRadius=0,maxRadius=0):
      """ finds the center of circular objects in image using hough circle transform
  
      Keyword arguments
      image -- uint8: numpy ndarray of a single image (no default).
      dp -- Inverse ratio of the accumulator resolution to the image resolution (default 1.7).
      minDist -- Minimum distance in pixel distance between the centers of the detected circles (default 100).
      param1 -- First method-specific parameter (default = 50).
      param2 -- Second method-specific parameter (default = 50).
      minRadius -- Minimum circle radius in pixel distance (default = 0).
      maxRadius -- Maximum circle radius in pixel distance (default = 0).
  
      Output
      center -- tuple: (x,y).
      radius -- int : radius.
      ERROR if circle is not detected. returns(-1) in this case    
      """
  
      circles=cv2.HoughCircles(image, 
                               cv2.HOUGH_GRADIENT, 
                               dp = dp, 
                               minDist = minDist, 
                               param1=param1, 
                               param2=param2, 
                               minRadius=minRadius, 
                               maxRadius=maxRadius)
      if circles is not None:
              circles = circles.reshape(circles.shape[1],circles.shape[2])
              return(circles)
      else:
          raise ValueError("ERROR!!!!!! circle not detected try tweaking the parameters or the min and max radius")
  
  def find_od(image_path_list):
      image_path_list.sort()
      print(len(image_path_list))
      result_df = pd.DataFrame(columns=["component_name","measured_dia_pixels","center_in_pixels"])
      for i,name in enumerate(image_path_list):
          img = cv2.imread(name,0) # read the image in grayscale
          ret,thresh_img = cv2.threshold(img, 50, 255, cv2.THRESH_BINARY_INV)
          thresh_img = cv2.bilateralFilter(thresh_img,5,91,91) #smoothing
          edges = cv2.Canny(thresh_img,100,200)
          circles = find_circles(edges,dp=1.7,minDist=100,param1=50,param2=30,minRadius=685,maxRadius=700)
          circles = np.squeeze(circles)
          result_df.loc[i] = os.path.basename(name),circles[2]*2,(circles[0],circles[1])
      result_df.sort_values("component_name",inplace=True)
      result_df.reset_index(drop=True,inplace=True)
      return(result_df)
  
  df = find_od(glob.glob("./images/*"))
  mean_d = df.measured_dia_pixels.mean()
  std_deviation = np.sqrt(np.mean(np.square([abs(x-mean_d) for x in df.measured_dia_pixels])))
  
  mm_per_pixel = 0.042
  print(std_deviation * mm_per_pixel)
  </code></pre>
  
  <p><strong>OUTPUT: 0.024</strong> </p>
  
  <p>The image of the component:</p>
  
  <p><a href="https://i.stack.imgur.com/N6uqw.jpg" rel="noreferrer"><img src="https://i.stack.imgur.com/N6uqw.jpg" alt="enter image description here"></a></p>
  
  <p>Since the Images are taken without disturbing the setup, I expect the measurement's repeatability to be around 0.005 mm (5 microns) (For 100 images).But this is not so. Is it a problem of hough circle? or what am I missing here</p>