rotate() creates huge working canvases
Posted: 2011-07-24T11:40:09-07:00
Hi,
I'm using IM 6.7.1 Q16 MT dll distribution, but the issue was observed in 6.5.2, both on MSVS 2005 and 2010. I have a 10MP input image and on calling rotate() it would occasionally start swapping out to disk and rotate() never completed. This is with your pre-built binary distribution. So on building the IM libraries ( 32-bit platform, DLL, MT, Q16 ) from source, I had the same issue. Then built the debug version of everything and traced execution through to see what was happening. I found that when rotate() expands the size of the image to accommodate rotation, it creates an enormous border region, even for very small rotation angles. For example a 1500 pixel border would be added when perhaps only 50 pixels was needed. Here is what I saw walking through the code:
Input image is 3840x2880 PNM 16-bit / channel. Walking through the IM code, we get to shear.c
At this point:
width =3840
height=2880
matching the actual image dimensions in the source PNM file. So far so good.
OK, so
y_width = 3941
so this means the shear.x factor is being computed properly. It also makes sense. As the trig works out for the -4.00 degree rotation we are doing. So far so good. However, look at the offset values which directly dictate the border placed around the original...
x_offset = 2020
y_offset = 1577
So we add a 2020 x 1577 border to the image? We only rotated it by 4 degrees, the needed border to fit the new image might be perhaps 100 pixels.
On return,
rotate_image.columns = 7880 ( i.e. 2020 + 3840 + 2020 )
rotate_image.rows = 6034 ( i.e. 1577 + 2880 + 1577 )
So we end up using a 48MP intermediate image to rotate() a 10MP image. This is why I was having issues with rotate, it would ask for a 300MB allocation on top of the couple copies of the image already in RAM ( each 10MP x 6 bytes ), and if Windows could not oblige, it would swap. BorderImage() does complete. However, when the actual rotation is performed in the next few lines, since its rotating a 48MP image held in disk swap, it never completes in our lifetime. You see Windows flail between periods of intense disk I/O and intense CPU, and rotate() never returns.
So is this WAD? Or can the intermediate canvas be trimmed down a bit? If it can, it would reduce the memory footprint of rotate() by 80%
-- Bob
I'm using IM 6.7.1 Q16 MT dll distribution, but the issue was observed in 6.5.2, both on MSVS 2005 and 2010. I have a 10MP input image and on calling rotate() it would occasionally start swapping out to disk and rotate() never completed. This is with your pre-built binary distribution. So on building the IM libraries ( 32-bit platform, DLL, MT, Q16 ) from source, I had the same issue. Then built the debug version of everything and traced execution through to see what was happening. I found that when rotate() expands the size of the image to accommodate rotation, it creates an enormous border region, even for very small rotation angles. For example a 1500 pixel border would be added when perhaps only 50 pixels was needed. Here is what I saw walking through the code:
Input image is 3840x2880 PNM 16-bit / channel. Walking through the IM code, we get to shear.c
Code: Select all
/*
Compute image size.
*/
width=image->columns;
height=image->rows;
if ((rotations == 1) || (rotations == 3))
{
width=image->rows;
height=image->columns;
}
width =3840
height=2880
matching the actual image dimensions in the source PNM file. So far so good.
Code: Select all
y_width=width+(ssize_t) floor(fabs(shear.x)*height+0.5);
x_offset=(ssize_t) ceil((double) width+((fabs(shear.y)*height)-width)/2.0-
0.5);
y_offset=(ssize_t) ceil((double) height+((fabs(shear.y)*y_width)-height)/2.0-
0.5);
y_width = 3941
so this means the shear.x factor is being computed properly. It also makes sense. As the trig works out for the -4.00 degree rotation we are doing. So far so good. However, look at the offset values which directly dictate the border placed around the original...
x_offset = 2020
y_offset = 1577
Code: Select all
/*
Surround image with a border.
*/
integral_image->border_color=integral_image->background_color;
integral_image->compose=CopyCompositeOp;
border_info.width=(size_t) x_offset;
border_info.height=(size_t) y_offset;
Code: Select all
rotate_image=BorderImage(integral_image,&border_info,exception);
rotate_image.columns = 7880 ( i.e. 2020 + 3840 + 2020 )
rotate_image.rows = 6034 ( i.e. 1577 + 2880 + 1577 )
So we end up using a 48MP intermediate image to rotate() a 10MP image. This is why I was having issues with rotate, it would ask for a 300MB allocation on top of the couple copies of the image already in RAM ( each 10MP x 6 bytes ), and if Windows could not oblige, it would swap. BorderImage() does complete. However, when the actual rotation is performed in the next few lines, since its rotating a 48MP image held in disk swap, it never completes in our lifetime. You see Windows flail between periods of intense disk I/O and intense CPU, and rotate() never returns.
So is this WAD? Or can the intermediate canvas be trimmed down a bit? If it can, it would reduce the memory footprint of rotate() by 80%
-- Bob