title: How to detect edge if edges of an object has same colour as background
body: <p>I am tasked to build a license plate detection system and my code does not work if the plate has the same colour of the paint of car (background).</p>
  
  <p>Take a look at this picture below.
  <a href="https://i.stack.imgur.com/FpxKi.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/FpxKi.png" alt="enter image description here"></a></p>
  
  <p>I have tried a variety of edge detection technique and my findings are they hardly work.</p>
  
  <p>Here is my image processing pipeline:</p>
  
  <ul>
  <li>Extract the gray channel from the image.</li>
  <li>Reduce noise with Iterative Bilaterial Filtering</li>
  <li>Detect edges with Adaptive Thresholding</li>
  <li>Dilate the edges slightly </li>
  <li>Locate contours based on some heuristics.</li>
  </ul>
  
  <p>The edge detection part performed miserably around the plate region.</p>
  
  <p><a href="https://i.stack.imgur.com/PpA7Q.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/PpA7Q.png" alt="enter image description here"></a></p>
  
  <p>The pipeline works good and I am able to detect license plates if the car is has a different paint colour than the plate.</p>
  
  <p><strong>Code</strong></p>
  
  <pre><code>def rectangleness(hull):
      rect = cv2.boundingRect(hull)
      rectPoints = np.array([[rect[0], rect[1]], 
                             [rect[0] + rect[2], rect[1]],
                             [rect[0] + rect[2], rect[1] + rect[3]],
                             [rect[0], rect[1] + rect[3]]])
      intersection_area = cv2.intersectConvexConvex(np.array(rectPoints), hull)[0] 
      rect_area = cv2.contourArea(rectPoints)
      rectangleness = intersection_area/rect_area
      return rectangleness
  
  
  def preprocess(image):
      image = imutils.resize(image, 1000)
  
      # Attenuate shadows by using H channel instead of converting to gray directly
      imgHSV = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
      _, _, gray = cv2.split(imgHSV)
  
      # Reduce noise while preserve edge with Iterative Bilaterial Filtering
      blur = cv2.bilateralFilter(gray, 11, 6, 6)
  
      # Detect edges by thresholding
      edge = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 5)
  
      # Dilate edges, kernel size cannot be too big as some fonts are very closed to the edge of the plates
      kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2, 2))
      dilated = cv2.dilate(edge, kernel)
  
      # Detect contours
      edge, contours, _ = cv2.findContours(dilated, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
  
      # Loop through contours and select the most probable ones
      contours = sorted(contours, key = cv2.contourArea, reverse=True)[:10]
  
      for contour in contours:
          perimeter = cv2.arcLength(contour, closed=True)
          approximate = cv2.approxPolyDP(contour, 0.02*perimeter, closed=True)
  
          if len(approximate) == 4:
              (x, y, w, h) = cv2.boundingRect(approximate)
              whRatio = w / h
  
              # Heuristics:
              # 1. Width of plate should at least be 2x greater than height
              # 2. Width of contour should be more than 5 (eliminate false positive)
              # 3. Height must not be too small
              # 4. Polygon must resemble a rectangle
              if (2.0 &lt; whRatio &lt; 6.0) and (w &gt; 5.0) and (h &gt; 20):
                  hull = cv2.convexHull(approximate, returnPoints=True)
                  if rectangleness(hull) &gt; 0.75:
                      print("X Y {} {}".format(x, y))
                      print("Height: {}".format(h))
                      print("Width : {}".format(w))
                      print("Ratio : {}\n".format(w/h))
                      cv2.drawContours(image, [approximate], -1, (0, 255, 0), 2)
  
      cv2.imshow("Edge", edge)
      cv2.imshow("Frame", image)
      cv2.waitKey(0)
      cv2.destroyAllWindows()
  </code></pre>