title: Show only large differences between images of slightly different scale
body: <p>I am trying to show differences between 2 images, mainly those within a &quot;cloud&quot; outline as shown in the <strong>new image</strong> below. The &quot;cloud&quot; outline represents changes that have been made to the <strong>reference image</strong>. Some info has been blanked out due to privacy reasons, do let me know if you require any more information!</p>
  <p>Reference Image: <a href="https://i.stack.imgur.com/09Zw8.jpg" rel="nofollow noreferrer">reference</a></p>
  <p>New Image: <a href="https://i.stack.imgur.com/oG1Kl.jpg" rel="nofollow noreferrer">new</a></p>
  <p>The scale of both images is a tad bit off, so I used cv2.homography but was unable to get it aligned properly</p>
  <p>Aligned Image: <a href="https://i.stack.imgur.com/ipQ7y.jpg" rel="nofollow noreferrer">aligned new</a></p>
  <p>Here's the output image from my code: <a href="https://i.stack.imgur.com/KgkCu.jpg" rel="nofollow noreferrer">result</a></p>
  <p>Image result I am trying to achieve: <a href="https://i.stack.imgur.com/dRRc9.jpg" rel="nofollow noreferrer">ideal result</a></p>
  <p>To summarize, here are the problems I am trying to solve</p>
  <ul>
  <li>To align 2 images of slightly different scale</li>
  <li>To highlight differences, typically within a cloud outline</li>
  <li>Merge small differences of close proximity into a larger boundary</li>
  </ul>
  <p>My code is based on the response of user fmw42 in this thread with some minor changes:
  <a href="https://stackoverflow.com/questions/66953562/find-differences-between-images-with-opencv-python">Find Differences Between Images with OpenCV Python</a></p>
  <p>Any help is greatly appreciated!</p>
  <pre><code>import cv2
  import numpy as np
   
  MAX_FEATURES = 500
  GOOD_MATCH_PERCENT = 0.1
    
  def alignImages(imNew, imRef):
  
      # im2 is reference and im1 is to be warped to match im2
      # note: numbering is swapped in function
      
      # Convert images to grayscale
      imNew_gray = cv2.cvtColor(imNew, cv2.COLOR_BGR2GRAY)
      imRef_gray = cv2.cvtColor(imRef, cv2.COLOR_BGR2GRAY)
     
      # Detect ORB features and compute descriptors.
      orb = cv2.ORB_create(MAX_FEATURES)
      keypoints1, descriptors1 = orb.detectAndCompute(imNew_gray, None)
      keypoints2, descriptors2 = orb.detectAndCompute(imRef_gray, None)
     
      # Match features.
      matcher = cv2.DescriptorMatcher_create(cv2.DESCRIPTOR_MATCHER_BRUTEFORCE_HAMMING)
      matches = list(matcher.match(descriptors1, descriptors2, None))
      
      # Sort matches by score
      matches.sort(key=lambda x: x.distance, reverse=False)
   
      # Remove not so good matches
      numGoodMatches = int(len(matches) * GOOD_MATCH_PERCENT)
      matches = matches[:numGoodMatches]
   
      # Draw top matches
      imMatches = cv2.drawMatches(imNew, keypoints1, imRef, keypoints2, matches, None)
      cv2.imwrite(&quot;matches.png&quot;, imMatches)
     
      # Extract location of good matches
      points1 = np.zeros((len(matches), 2), dtype=np.float32)
      points2 = np.zeros((len(matches), 2), dtype=np.float32)
   
      for i, match in enumerate(matches):
          points1[i, :] = keypoints1[match.queryIdx].pt
          points2[i, :] = keypoints2[match.trainIdx].pt
     
      # Find homography
      h, mask = cv2.findHomography(points1, points2, cv2.RANSAC)
   
      # Use homography
      height, width, channels = imRef.shape
      imReg = cv2.warpPerspective(imNew, h, (width, height))
     
      return imReg, h
  
      
  # Read reference image
  refFilename = &quot;SD5021-.png&quot;
  print(&quot;Reading reference image : &quot;, refFilename)
  imReference = cv2.imread(refFilename)
  hh, ww = imReference.shape[:2]
  
  # Read image to be aligned
  imFilename = &quot;SD5021A.png&quot;
  print(&quot;Reading image to align : &quot;, imFilename);  
  im = cv2.imread(imFilename)
  
  # Aligned image will be stored in imReg. 
  # The estimated homography will be stored in h. 
  imReg, h = alignImages(im, imReference)
  
  # Print estimated homography
  print(&quot;Estimated homography : \n&quot;,  h)
  
  # Convert images to grayscale
  ref_gray = cv2.cvtColor(imReference, cv2.COLOR_BGR2GRAY)
  im_gray = cv2.cvtColor(imReg, cv2.COLOR_BGR2GRAY)
  
  # Otsu threshold
  refThresh = cv2.threshold(ref_gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
  imThresh = cv2.threshold(im_gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
  
  # apply morphology open and close
  kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7,7))
  refThresh = cv2.morphologyEx(refThresh, cv2.MORPH_OPEN, kernel, iterations=1)
  refThresh = cv2.morphologyEx(refThresh, cv2.MORPH_CLOSE, kernel, iterations=1).astype(np.float64)
  imThresh = cv2.morphologyEx(imThresh, cv2.MORPH_OPEN, kernel, iterations=1).astype(np.float64)
  imThresh = cv2.morphologyEx(imThresh, cv2.MORPH_CLOSE, kernel, iterations=1)
  
  # get absolute difference between the two thresholded images
  diff = np.abs(cv2.add(imThresh,-refThresh))
  
  # apply morphology open to remove small regions caused by slight misalignment of the two images
  kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (20,20))
  diff_cleaned = cv2.morphologyEx(diff, cv2.MORPH_OPEN, kernel, iterations=1).astype(np.uint8)
  
  # Filter using contour area and draw bounding boxes that do not touch the sides of the image
  cnts = cv2.findContours(diff_cleaned, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  cnts = cnts[0] if len(cnts) == 2 else cnts[1]
  result = im.copy()
  for c in cnts:
      x,y,w,h = cv2.boundingRect(c)
      if x&gt;0 and y&gt;0 and x+w&lt;ww-1 and y+h&lt;hh-1:
          cv2.rectangle(result, (x, y), (x+w, y+h), (0, 0, 255), 2)
  
  # save images
  cv2.imwrite('new_aligned.jpg', imReg)
  cv2.imwrite('diff.png', diff_cleaned)
  cv2.imwrite('results.png', result)
  </code></pre>