How to do Cropping and Scaling Using Javascript

By Isak - March 11, 2016 (Last updated: April 9, 2017)

The Vidispine transcoder first crops, then rotates, then scales. This post is about how to use Javascript and some equations to emulate doing scaling before cropping. Brush up on your math skills and dive right in.

Let’s look at the details of the previous blog post Using scripts to automatically fit video.

As said in the post above, the order of the operations is crop, rotate, scale. Assume this image (or video):

dynamic cropping illustration #1

The goal is to rotate this image into the right orientation, and by cropping and padding we will keep the aspect ratio. Finally we would like a 1920×1080 output. First the crop. We crop the sides by 150 pixels each, and pad the top and bottom with 511 pixels each.

dynamic cropping illustration #2

Then rotate the image. We now have:

dynamic cropping illustration #3

You see that 1742:980 is very close to 16:9. Now we can scale the image.

dynamic cropping illustration #4

The shape tag for the operation above:

       <scaling>
            <width>1920</width>
            <height>1080</height>
            <top>-511</top>
            <bottom>-511</bottom>
            <left>150</left>
            <right>150</right>
            <padColor>#000080</padColor>
            <pixelAspectRatio>
                <horizontal>1</horizontal>
                <vertical>1</vertical>
            </pixelAspectRatio>
        </scaling>

 

So what if we wanted to scale first, then crop? Fortunately you can switch the order of the operations yourself, with a little bit of algebra. Above, we did:

  • Original dimension: x
  • Crop of the two sides: c1, c2
  • Scaling to size: x

If you start with an image that is 1000 pixels wide, and you want to scale it down to 500 pixels, then crop 100 pixels from each side, that is equivalent to:

  • Crop by 100 · 1000 / 500 = 200 pixels on each side. That gives you an image that is 1000 – 200 – 200 = 600 pixels.
  • Then scale the image to 300 pixels.

What if you know what the scaled dimension should be after cropping, and you know how much to crop in the scaled dimension? The scaling of the cropped dimension is x‘ / (xc1 – c2). That means, the cropped part in the scaled dimension is

  • c1‘ = c1 · x‘ / (xc1 – c2)
  • c2‘ = c2 · x‘ / (xc1 – c2)

Solving this equation system gives:

  • c1 = c1‘ · / ( c1‘ + c2‘ + x‘ )
  • c2 = c2‘ · / ( c1‘ + c2‘ + x‘ )

Or, in English. If you have an image that is 1000 pixels wide and you want to scale the image. After scaling, you want to crop 100 pixels from each side, and then the result to be 300 pixels:

  • Crop by 100 · 1000 / ( 100 + 100 + 300 ) = 200 pixels on each side. That gives you an image that is 1000 – 200 – 200 = 600 pixels.
  • Then scale the image to 300 pixels.

Ok, another example. Let’s say you have an video, w × h. We want to scale the height so it is 900 pixels. Then crop the sides so the resulting video is 1600 pixels wide.

The scaling of the video is 900 / h. That means that the scaled width is w‘ = 900 / h · w. So, in the scaled dimension what we want to crop is ( 1600 – w‘ ) / 2 on each side. But cropping is done before scaling. With the equation above, this is what the script could look like:

var inWidth = shape.getVideoComponent().get(0).getResolution().getWidth();
var inHeight = shape.getVideoComponent().get(0).getResolution().getHeight();
var scaling = preset.getVideo().getScaling();
 
var scaledWidth = scaling.getWidth() / inHeight * inWidth;
var cropAfterScale = (scaling.getHeight() - scaledWidth) / 2:
var cropBeforeScale = cropAfterScale * inWidth / (2 * cropAfterScale + scaledWidth);

scaling.setWidth(scaledWidth);
scaling.setLeft(cropBeforeScale);
scaling.setRight(cropBeforeScale);

Use this with a preset like:

        <scaling>
            <width>1600</width>
            <height>900</height>
            <pixelAspectRatio>
                <horizontal>1</horizontal>
                <vertical>1</vertical>
            </pixelAspectRatio>
        </scaling>

 

Categories

TechnologyVidispine