Search code examples
rotationimagemagickcrop

imagemagick Obtain a rectangle by two corners and a point on the opposite edge


Suppose I have a scanned image. The green part is what valid for me, and I would like to obtain that part from the oringal. Right now, I am doing a lot of manual calculation and then using "-rotate" then "-crop" to obtain what I want. Is there a way to obtain it from coordinates of A, B, and C? What I can think of is to write something in bash to

  1. calculate the slope from coorindates of A and B
  2. calculate the angle for rotation
  3. rotate
  4. calculate new coordinates for A, B, and C
  5. calculate coordiantes for the other corners
  6. crop

However, is this re-inventing the wheel? Please point me to the right directions.

Because there are lots of background noices of different levels from different scans, it is not possible to use "-deskew".

enter image description here


Solution

  • NOTE: See the "EDITED TO ADD:" section below for a more complete answer.

    ImageMagick can use its built FX expressions to do some pretty extensive calculations. If the coordinates of the points are known, this simple command shows how to get started. It's in Windows syntax, and uses a "+distort" operation with an FX expression to, in effect, rotate the image to align points "A" and "B" on the same horizontal while maintaining the length of AB...

    magick input.png ^
       -set option:ax 50 ^
       -set option:ay 46 ^
       -set option:bx 472 ^
       -set option:by 82 ^
       -virtual-pixel white ^
       +distort affine "%[ax],%[ay] %[ax],%[ay] %[bx],%[by] %[fx:hypot(bx-ax,by-ay)+ax],%[ay]" ^
       result.png
    

    That creates this result from your example image...

    enter image description here

    The coordinates for the points could be sent into the command as variables in a script.

    The eventual "-crop" operation would start at "ax,ay" and use "hypot(bx-ax,by-ay)" as the width. See the "EDITED TO ADD:" section below for an example.

    EDITED TO ADD:

    With a few additions to the above command it can also include a "-crop" operation so that point "C" defines the bottom of the resulting image. This command measures the lengths of the sides of the triangle, and uses that to calculate the altitude of the finished output.

    magick input.png ^
       -set option:ax 50 ^
       -set option:ay 46 ^
       -set option:bx 472 ^
       -set option:by 82 ^
       -set option:cx 88 ^
       -set option:cy 342 ^
       -set option:ab %[fx:hypot(bx-ax,by-ay)] ^
       -set option:ac %[fx:hypot(cx-ax,cy-ay)] ^
       -set option:bc %[fx:hypot(cx-bx,cy-by)] ^
       -set option:v1 %[fx:(ab+ac+bc)/2] ^
       +distort affine "%[ax],%[ay] %[ax],%[ay] %[bx],%[by] %[fx:ab+ax],%[ay]" ^
       -crop %[ab]x%[fx:2*sqrt(v1*(v1-ab)*(v1-ac)*(v1-bc))/ab]+%[ax]+%[ay] ^
       +repage ^
          result.png
    

    That sets variables "ab", "ac", and "bc" for the line lengths of AB, AC, and BC, and sets an intermediate variable "v1" to tidy the calculation of the altitude.

    This is the result of running that command on your sample image...

    enter image description here

    Read more on using FX expressions here... ImageMagick FX Expressions