title: How to translate points on image after cropping it and resizing it?
body: <p>I am creating a program which allows a user to annotate images with points.
  This program allows user to zoom in an image so user can annotate more precisely.</p>
  <p>Program zooms in an image doing the following:</p>
  <ol>
  <li>Find the center of image</li>
  <li>Find minimum and maximum coordinates of new cropped image relative to center</li>
  <li>Crop image</li>
  <li>Resize the image to original size</li>
  </ol>
  <p>For this I have written the following Python code:</p>
  <pre class="lang-py prettyprint-override"><code>import cv2
  def zoom_image(original_image, cut_off_percentage, list_of_points):
      height, width = original_image.shape[:2]
      center_x, center_y = int(width/2), int(height/2)
      half_new_width = center_x - int(center_x * cut_off_percentage)
      half_new_height = center_y - int(center_y * cut_off_percentage)
      min_x, max_x = center_x - half_new_width, center_x + half_new_width
      min_y, max_y = center_y - half_new_height, center_y + half_new_height
  
       #I want to include max coordinates in new image, hence +1
      cropped = original_image[min_y:max_y+1, min_x:max_x+1]
      new_height, new_width = cropped.shape[:2]
  
      resized = cv2.resize(cropped, (width, height))
      translate_points(list_of_points, height, width, new_height, new_width, min_x, min_y)
  </code></pre>
  <p>I want to <em>resize</em> the image to original width and height so user always works on same &quot;surface&quot;
  regardless of how zoomed image is.</p>
  <p>The problem I encounter is how to correctly scale points (annotations) when doing this. My algorithm to do so was following:</p>
  <ol>
  <li>Translate points on original image by subtracting <code>min_x</code> from x coordinate and <code>min_y</code> from y coordinate</li>
  <li>Calculate constants for scaling x and y coordinates of points</li>
  <li>Multiply coordinates by constants</li>
  </ol>
  <p>For this I use the following Python code:</p>
  <pre class="lang-py prettyprint-override"><code>import cv2
  def translate_points(list_of_points, height, width, new_height, new_width, min_x, min_y):
      #Calculate constants for scaling points
      scale_x, scale_y = width / new_width, height / new_height
  
      #Translate and scale points
      for point in list_of_points:
          point.x = (point.x - min_x) * scale_x
          point.y = (point.y - min_y) * scale_y
  </code></pre>
  <p>This code <strong>doesn't</strong> work. If I zoom in once, it is hard to detect the offset of pixels but it happens. If I keep zooming in, it will be much easier to detect the &quot;drift&quot; of points. Here are images to provide examples. On original image (1440x850) I places a point in the middle of blue crosshair. The more I zoom in the image it is easier to see that algorithm doesn't work with bigger cut-ofs.</p>
  <p><a href="https://i.stack.imgur.com/UrWDE.png" rel="nofollow noreferrer">Original image. Blue crosshair is middle point of an image. Red angles indicate what will be borders after image is zoomed once</a></p>
  <p><a href="https://i.stack.imgur.com/BomzW.png" rel="nofollow noreferrer">Image after zooming in once.</a></p>
  <p><a href="https://i.stack.imgur.com/4xC0B.png" rel="nofollow noreferrer">Image after zooming in 5 times. Clearly, green point is no longer in the middle of image</a></p>
  <p>The <code>cut_off_percentage</code> I used is 15% (meaning that I keep 85% of width and height of original image, calculated from the center).</p>
  <p>I have also tried the following library: <a href="https://github.com/sandesha-hegde/augmentit" rel="nofollow noreferrer">Augmentit python library</a>
  Library has functions for cropping images and resizing them together with points. Library also causes the points to drift. This is expected since the code I implemented and library's functions use the same algorithm.</p>
  <p>Additionally, I have checked whether this is a rounding problem. It is <strike><em>not</em>.</strike> Library rounds the points after multiplying coordinates with scales. Regardless on how they are rounded, points are still off by 4-5 px. This increases the more I zoom in the picture.</p>
  <p><strong>EDIT</strong>: A more detailed explanation is given here since I didn't understand a given answer.</p>
  <p>The following is an image of right human hand.</p>
  <p><a href="https://i.stack.imgur.com/oxTix.png" rel="nofollow noreferrer">Image of a hand in my program</a></p>
  <p>Original dimension of this image is 1440 pixels in width and 850 pixels in height. As you can see in this image, I have annotated right wrist at location (756.0, 685.0). To check whether my program works correctly, I have opened this exact image in GIMP and placed a white point at location (756.0, 685.0). The result is following:</p>
  <p><a href="https://i.stack.imgur.com/9CNMO.png" rel="nofollow noreferrer">Image of a hand in GIMP</a></p>
  <p>Coordinates in program work correctly. Now, if I were to calculate parameters given in first answer according to code given in first answer I get following:</p>
  <p><code>vec = [756, 685]</code>
  <code>hh = 425</code>
  <code>hw = 720</code>
  <code>cov = [720, 425]</code></p>
  <p>These parameters make sense to me. Now I want to zoom the image to scale of 1.15. I crop the image by choosing center point and calculating <code>low</code> and <code>high</code> values which indicate what rectangle of image to keep and what to cut. On the following image you can see what is kept after cutting (everything inside red rectangle).</p>
  <p><a href="https://i.stack.imgur.com/L85bE.png" rel="nofollow noreferrer">What is kept when cutting</a></p>
  <p>Lows and highs when cutting are:
  <code>xb = [95,1349]</code>
  <code>yb = [56,794]</code></p>
  <p>Size of cropped image: 1254 x 738</p>
  <p>This cropped image will be resized back to original image. However, when I do that my annotation gets completely wrong coordinates when using parameters described above.</p>
  <p><a href="https://i.stack.imgur.com/iqxTe.png" rel="nofollow noreferrer">After zoom</a></p>
  <p>This is the code I used to crop, resize and rescale points, based on the first answer:</p>
  <pre class="lang-py prettyprint-override"><code>width, height = image.shape[:2]
  center_x, center_y = int(width / 2), int(height / 2)
  scale = 1.15
  scaled_width = int(center_x / scale)
  scaled_height = int(center_y / scale)
  xlow = center_x - scaled_width
  xhigh = center_x + scaled_width
  ylow = center_y - scaled_height
  yhigh = center_y + scaled_height
  xb = [xlow, xhigh]
  yb = [ylow, yhigh]
  cropped = image[yb[0]:yb[1], xb[0]:xb[1]]
  resized = cv2.resize(cropped, (width, height), cv2.INTER_CUBIC)
  
  #Rescaling poitns
  cov = (width / 2, height / 2)
  width, height = resized.shape[:2]
  hw = width / 2
  hh = height / 2
  for point in points:
      x, y = point.scx, point.scy
      x -= xlow
      y -= ylow
      x -= cov[0] - (hw / scale)
      y -= cov[1] - (hh / scale)
      x *= scale
      y *= scale
      x = int(x)
      y = int(y)
      point.set_coordinates(x, y)
  </code></pre>