From: Paul K. <pki...@us...> - 2005-02-10 12:09:10
|
Todd, octave-dev is indeed active, and I did see your post on imrotate. I don't see anything about imresize. help-octave and bug-octave are much more active. Your patch looks okay, though I would do mod 360 regardless of whether theta was negative. I'm disappointed that the current imrotate gives an incorrect result you showed in your example, and would like to see that addressed as well. For example, what happens if the angle is 269.999999? Discretizing the angle before checking if it is 270 would be useful. Thanks, - Paul On Feb 9, 2005, at 11:08 PM, Todd Neal wrote: > Hello, > > I saw on octave-maintainers that you are involved in Octave-forge. I > sent an email to the octave-dev list on Sourceforge and haven't > recevied any responses. Is there a more active list ? > > > I am also working on imresize.m clone for Octave and wanted to discuss > it with someone else who was interested. > > > Thanks, Todd > |
From: Justus P. <Jus...@UL...> - 2005-02-22 12:09:57
|
Paul, Paul Kienzle <pki...@us...> wrote on Sat, 19 Feb 2005 15:47:00 -0500: > Here are some more tests you might include: > > % Make sure the dithering preserves pixel density > t=3Dzeros(1,5); > for n=3D1:15; t(n) =3D sum(imrotate(eye(n),20,'bilinear')(:)); end > assert(t,[1:15]); This test in fact works. The only difference is that my code always produces a slightly smaller sum because it does not pad the image (see my previous message for a brief discussion). With growing image sizes and fixed rotation angle, the difference is bounded: assert(t,[1:n], 2.2) should pass for any n. > % Make sure the size matches the expected size assuming square pixels. > angle=3D20; t=3Dzeros(15,2); > for n=3D1:15; t(n,:) =3D size(imrotate(ones(n,3*n),angle,'bilinear'));= end > a =3D angle*pi/180; > x =3D ceil([(1:n);3*(1:n)]'*[cos(a) sin(a);sin(a) cos(a)]); > assert(t,x); If you insist... the code for computing the image size is trivial. But to be fair, use round() because this is what imrotate does. In that case, a tolerance of 1 is sufficient to pass the assert(), except for the very first element. But I really would not include this test. It is only a rough estimate, the code in imrotate is at least as simple and does it better. Justus --=20 Justus H. Piater, Ph.D. http://www.montefiore.ulg.ac.be/~piater/ Institut Montefiore, B28 Phone: +32-4-366-2279 Universit=E9 de Li=E8ge, Belgium Fax: +32-4-366-2620 |
From: Justus P. <Jus...@UL...> - 2005-02-14 13:39:23
Attachments:
testimrotate.tgz
|
Hi, I've fixed the problems reported by Todd Neal, and added support for rot90(). Note that for "crop" one has to do the cropping after the rot90(). Moreover, if the image dimensions are not of equal parity (odd width and even height, or vice versa), a +/- 90 degree rotation entails interpolation. This implies that rot90() cannot be used for such rotations. The new code is designed to behave smoothly across small increments of rotation angle, rot90 or not. The result should be exactly the same, whether or not rot90 is used. Please try out the code (Todd!) and tell me what you think. Once I have Todd's approval, this code (imrotate.m and imrotate_Fourier.m) should replace the existing version of imrotate in CVS. There are two test functions, testimrotate and testrot90. Look at their code and play with them. The other files are needed by these two. Paul Kienzle <pki...@us...> wrote on Thu, 10 Feb 2005 07:09:00 -0500: > I'm disappointed that the current imrotate gives an incorrect result you > showed in your example, and would like to see that addressed as well. Just to clarify: The corners were off by half a pixel due to a round-off error. This error is dramatic for small matrices, but barely noticeable for anything that merits being called an "image". Nevertheless, bug is bug... and is fixed. > For example, what happens if the angle is 269.999999? Discretizing > the angle before checking if it is 270 would be useful. I strongly disagree. rot90 should only be used for _exact_ multiples of 90 degrees. If not, how do we choose the tolerance? If the image is large enough, the result will be different. Let the user decide. The user can discretize the angle before calling imrotate() if s/he wants to. Todd Neal <to...@gm...> wrote on Thu, 10 Feb 2005 09:24:03 -0600: > I also modified the special cases so that it doesn't rely on rot90 Why? rot90 does exactly what's needed here. My code uses it happily. Todd Neal <to...@gm...> wrote on Thu, 10 Feb 2005 12:54:06 -0600: > I am working on imresize and would be interested in seeing the > bicubic interpolation code. Here it is :-). The bicubic interpolation code is very general and could be moved out of imrotate.m for use with imresize and others. However, I don't think this is the right way to go; there are more efficient algorithms for interpolation along parralel, regular grids, e.g. Robert G. Keys, Cubic Convolution Interpolation for Digital Image Processing, IEEE Trans. on Acoustics, Speech, and Signal Processing 29(6) 1981, 1153-1160. If you want to do the community a service, add such bicubic interpolation to interp2.m, and base imresize() on that. You may also want to check out "help rotate_scale". Good luck! Justus |
From: Todd N. <to...@gm...> - 2005-02-14 13:46:11
|
That sounds great, I'm going to try to get some time to take a close look at it. > Todd Neal <to...@gm...> wrote on Thu, 10 Feb 2005 09:24:03 -0600: > > > I also modified the special cases so that it doesn't rely on rot90 > > Why? rot90 does exactly what's needed here. My code uses it happily. I used array indexing and transposing to achieve the 90,180, and 270 degree rotations. This effectively saved a function call to rot90. I haven't done any benchmarks but just assumed it would be quicker this way. Todd |
From: Todd N. <to...@gm...> - 2005-02-15 20:14:36
|
On Sun, 13 Feb 2005 20:35:26 +0100, Justus Piater <Jus...@ul...> wrote: > Please try out the code (Todd!) and tell me what you think. Once I > have Todd's approval, this code (imrotate.m and imrotate_Fourier.m) > should replace the existing version of imrotate in CVS. > I've tried it out and it works fine, much better than the previous version. > There are two test functions, testimrotate and testrot90. Look at > their code and play with them. The other files are needed by these > two. > > Paul Kienzle <pki...@us...> wrote on Thu, 10 Feb 2005 > 07:09:00 -0500: > > > I'm disappointed that the current imrotate gives an incorrect result you > > showed in your example, and would like to see that addressed as well. > > Just to clarify: The corners were off by half a pixel due to a > round-off error. This error is dramatic for small matrices, but barely > noticeable for anything that merits being called an "image". > Nevertheless, bug is bug... and is fixed. I thought it may have been imperceptible with large images but am glad that you found a way fix it anyway, the fixes that I implemented were definitely not the greatest. > > For example, what happens if the angle is 269.999999? Discretizing > > the angle before checking if it is 270 would be useful. > > I strongly disagree. rot90 should only be used for _exact_ multiples > of 90 degrees. If not, how do we choose the tolerance? If the image is > large enough, the result will be different. Let the user decide. The > user can discretize the angle before calling imrotate() if s/he wants > to. > I agree with Justus on this point, for a very large image a small angle change will result in a different rotated image. However it may be possible to do something in the manner of this pseudocode: theta=89.99 cornersTheta = compute the corners for 89.99; cornersNinety = compute the corners for 90.00 if cornersTheta = cornersNinety theta = 90; end; The speedup for the few cases where the angle is so close to a multiple of 90 and the image is small enough that it makes no difference may not be worth the expense of doing the checks to determine this. > Todd Neal <to...@gm...> wrote on Thu, 10 Feb 2005 09:24:03 -0600: > > > I also modified the special cases so that it doesn't rely on rot90 > > Why? rot90 does exactly what's needed here. My code uses it happily. > I used array indexing and transposing to achieve the 90,180, and 270 degree rotations. If you look at rot90.m, the rotation is accomplished by these methods. Including the code in imrotate.m saves a function call to rot90 and should be a bit faster. Todd |
From: Paul K. <pki...@us...> - 2005-02-16 03:23:32
|
> >>> For example, what happens if the angle is 269.999999? Discretizing >>> the angle before checking if it is 270 would be useful. >> >> I strongly disagree. rot90 should only be used for _exact_ multiples >> of 90 degrees. If not, how do we choose the tolerance? If the image is >> large enough, the result will be different. Let the user decide. The >> user can discretize the angle before calling imrotate() if s/he wants >> to. >> > > I agree with Justus on this point, for a very large image a small > angle change will result in a different rotated image. However it may > be possible to do something in the manner of this pseudocode: > > theta=89.99 > cornersTheta = compute the corners for 89.99; > cornersNinety = compute the corners for 90.00 > > if cornersTheta = cornersNinety > theta = 90; > end; > > > The speedup for the few cases where the angle is so close to a > multiple of 90 and the image is small enough that it makes no > difference may not be worth the expense of doing the checks to > determine this. The reason you don't want to check for multiples of 90 is that floating point arithmetic is not exact. For the sake of argument, lets say that the rotation angle comes from acos(x) with x approximately 0: > acos(eps)/pi*180-90 ans = -1.4211e-14 This is an angle which is almost but not quite 90. For large matrices, the cost of testing the corners before choosing the algorithm is trivial compared to the rotation. For small matrices, processing time will dominated by interpreter speed and one or two extra lines amongst hundreds is again a small penalty. Whether or not this optimization is worthwhile depends on how frequently these near 90*n cases occur vs. speed gain from the simple operations. I'll leave that to the users of imrotate to decide. - Paul |
From: Paul K. <pki...@us...> - 2005-02-16 04:57:57
|
On Feb 13, 2005, at 2:35 PM, Justus Piater wrote: > I've fixed the problems reported by Todd Neal, and added support for > rot90(). Note that for "crop" one has to do the cropping after the > rot90(). Moreover, if the image dimensions are not of equal parity > (odd width and even height, or vice versa), a +/- 90 degree rotation > entails interpolation. This implies that rot90() cannot be used for > such rotations. > > The new code is designed to behave smoothly across small increments of > rotation angle, rot90 or not. The result should be exactly the same, > whether or not rot90 is used. > > Please try out the code (Todd!) and tell me what you think. Once I > have Todd's approval, this code (imrotate.m and imrotate_Fourier.m) > should replace the existing version of imrotate in CVS. > > There are two test functions, testimrotate and testrot90. Look at > their code and play with them. The other files are needed by these > two. > I replaced the old version of imrotate with what you sent. I would appreciate a few simple test cases which do not rely on a human visual system to determine if the result is correct. I tried the following: X = rand(30); Y = imrotate(imrotate(X,30,'bicubic'),-30,'bicubic'); norm(X(17+[1:5],17+[1:5])-Y(30+[1:5],30+[1:5])) ans = 0.46103 Using a smoother function worked better: X = peaks(30); Y = imrotate(imrotate(X,30,'bicubic'),-30,'bicubic'); norm(X(17+[1:5],17+[1:5])-Y(30+[1:5],30+[1:5])) ans = 0.0036647 Anyone have any better suggestions? Thanks, - Paul |
From: Todd N. <to...@gm...> - 2005-02-16 05:49:02
|
On Tue, 15 Feb 2005 23:58:04 -0500, Paul Kienzle <pki...@us...> wrote: > I replaced the old version of imrotate with what you sent. > > I would appreciate a few simple test cases which do not > rely on a human visual system to determine if the result > is correct. > > I tried the following: > > X = rand(30); > Y = imrotate(imrotate(X,30,'bicubic'),-30,'bicubic'); > norm(X(17+[1:5],17+[1:5])-Y(30+[1:5],30+[1:5])) > ans = 0.46103 > > Using a smoother function worked better: > > X = peaks(30); > Y = imrotate(imrotate(X,30,'bicubic'),-30,'bicubic'); > norm(X(17+[1:5],17+[1:5])-Y(30+[1:5],30+[1:5])) > ans = 0.0036647 > > Anyone have any better suggestions? > I was looking for a few good test cases and came across using this as a test case: imrotate(eye(20),45) The problem is displayed here (in a smaller case to fit in an email well): octave:18> imrotate(eye(13),45) ans = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 1 1 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Todd |
From: Jeff O. <jor...@cs...> - 2005-02-19 19:55:52
|
I'm not sure the bilinear option is working. imrotate(eye(13),45,"bilinear") and imrotate(eye(13),40,"bilinear") Jeff On Feb 16, 2005, at 12:48 AM, Todd Neal wrote: > On Tue, 15 Feb 2005 23:58:04 -0500, Paul Kienzle > <pki...@us...> wrote: >> I replaced the old version of imrotate with what you sent. >> >> I would appreciate a few simple test cases which do not >> rely on a human visual system to determine if the result >> is correct. >> >> I tried the following: >> >> X = rand(30); >> Y = imrotate(imrotate(X,30,'bicubic'),-30,'bicubic'); >> norm(X(17+[1:5],17+[1:5])-Y(30+[1:5],30+[1:5])) >> ans = 0.46103 >> >> Using a smoother function worked better: >> >> X = peaks(30); >> Y = imrotate(imrotate(X,30,'bicubic'),-30,'bicubic'); >> norm(X(17+[1:5],17+[1:5])-Y(30+[1:5],30+[1:5])) >> ans = 0.0036647 >> >> Anyone have any better suggestions? >> > > I was looking for a few good test cases and came across using this as > a test case: > > imrotate(eye(20),45) > > The problem is displayed here (in a smaller case to fit in an email > well): > > octave:18> imrotate(eye(13),45) > ans = > > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 1 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 1 > 1 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 1 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 > > > Todd |
From: Paul K. <pki...@us...> - 2005-02-19 20:47:09
|
Yes, imrotate seems to be broken for all of bilinear, bicubic and nearest. As Todd pointed out, 'nearest' is particularly glaring. Can the users of this function please come up with a fix which you are all happy with? Here are some more tests you might include: % Make sure the dithering preserves pixel density t=zeros(1,5); for n=1:15; t(n) = sum(imrotate(eye(n),20,'bilinear')(:)); end assert(t,[1:15]); % Make sure the size matches the expected size assuming square pixels. angle=20; t=zeros(15,2); for n=1:15; t(n,:) = size(imrotate(ones(n,3*n),angle,'bilinear')); end a = angle*pi/180; x = ceil([(1:n);3*(1:n)]'*[cos(a) sin(a);sin(a) cos(a)]); assert(t,x); Thanks, - Paul On Feb 19, 2005, at 2:55 PM, Jeff Orchard wrote: > > I'm not sure the bilinear option is working. > > imrotate(eye(13),45,"bilinear") > > and > > imrotate(eye(13),40,"bilinear") > > Jeff > > > On Feb 16, 2005, at 12:48 AM, Todd Neal wrote: > >> On Tue, 15 Feb 2005 23:58:04 -0500, Paul Kienzle >> <pki...@us...> wrote: >>> I replaced the old version of imrotate with what you sent. >>> >>> I would appreciate a few simple test cases which do not >>> rely on a human visual system to determine if the result >>> is correct. >>> >>> I tried the following: >>> >>> X = rand(30); >>> Y = imrotate(imrotate(X,30,'bicubic'),-30,'bicubic'); >>> norm(X(17+[1:5],17+[1:5])-Y(30+[1:5],30+[1:5])) >>> ans = 0.46103 >>> >>> Using a smoother function worked better: >>> >>> X = peaks(30); >>> Y = imrotate(imrotate(X,30,'bicubic'),-30,'bicubic'); >>> norm(X(17+[1:5],17+[1:5])-Y(30+[1:5],30+[1:5])) >>> ans = 0.0036647 >>> >>> Anyone have any better suggestions? >>> >> >> I was looking for a few good test cases and came across using this as >> a test case: >> >> imrotate(eye(20),45) >> >> The problem is displayed here (in a smaller case to fit in an email >> well): >> >> octave:18> imrotate(eye(13),45) >> ans = >> >> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 >> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 >> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 >> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 >> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 >> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 >> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 >> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 >> 1 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 1 >> 1 0 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 1 >> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 >> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 >> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 >> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 >> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 >> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 >> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 >> 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 >> >> >> Todd > |
From: Justus P. <Jus...@UL...> - 2005-02-21 18:26:16
|
I feel honored that my code receives this thorough testing :-) However, I don't see what's wrong with the code. Please explain to me what you expect imrotate(eye(13),45, "...") to look like. It is certainly *not* going to be a homogeneous horizontal line since we are interpolating pixels into a new raster whose spacing is stretched by sqrt(2). We will therefore see a symmetric structure of varying gray values between zero and one, and that is wider than one pixel. Remember that we're rotating images, not matrices. Convince me that the code is wrong by presenting a correct result, and I'll see into fixing it. Cheers, Justus Jeff Orchard <jor...@cs...> wrote on Sat, 19 Feb 2005 14:55:39 -0500: > I'm not sure the bilinear option is working. > imrotate(eye(13),45,"bilinear") > and > imrotate(eye(13),40,"bilinear") Paul Kienzle <pki...@us...> wrote on Sat, 19 Feb 2005 15:47:00 -0500: > Yes, imrotate seems to be broken for all of bilinear, bicubic > and nearest. As Todd pointed out, 'nearest' is particularly > glaring. --=20 Justus H. Piater, Ph.D. http://www.montefiore.ulg.ac.be/~piater/ Institut Montefiore, B28 Phone: +32-4-366-2279 Universit=E9 de Li=E8ge, Belgium Fax: +32-4-366-2620 |
From: Justus P. <Jus...@UL...> - 2005-02-22 11:32:44
|
Paul Kienzle <pki...@us...> wrote on Tue, 22 Feb 2005 01:09:38 -0500: > FWIW, matlab also exhibits the alternating bright-dim stripes but less > severely than yours. For imrotate(eye(...), ..., ...) I just verified that for all of "nearest", "bilinear" and "bicubic", Matlab's (6.5) and my code produce *exactly* the same result, modulo the following: - My code crops the image a little more aggressively than Matlab does. I round the size such that 90.1 and 90 degrees (e.g.) give the same result. Matlab uses the ceil. It's a matter of taste. Moreover, Matlab appears to pad the image with zeros, while I do not pad. Thus, Matlab fills in more values/produces a larger image. However, I do not see why we should pad; the value of zero is about as arbitrary as any other value. My code fills in (the arbitrary value of) zero wherever the result is not defined. Again, it's a matter of taste, but my method yields simpler code. - Both Matlab and my code may produce an odd-size result image for an even-size source image, and vice versa. In those cases where Matlab's and my code produce resulting image sizes of opposing parity, the results are obviously not identical, but neither is more "correct" than the other. Justus --=20 Justus H. Piater, Ph.D. http://www.montefiore.ulg.ac.be/~piater/ Institut Montefiore, B28 Phone: +32-4-366-2279 Universit=E9 de Li=E8ge, Belgium Fax: +32-4-366-2620 |
From: Paul K. <pki...@us...> - 2005-02-22 12:13:52
|
Justus, I was testing the wrong code. When I look at it now it looks fine. Sorry to trouble you. I would still like you to tell me which tests you prefer. I know I've thrown a lot of suggestions your way :-) Thanks, - Paul On Feb 22, 2005, at 6:32 AM, Justus Piater wrote: > Paul Kienzle <pki...@us...> wrote on Tue, 22 Feb 2005 > 01:09:38 -0500: > >> FWIW, matlab also exhibits the alternating bright-dim stripes but = less >> severely than yours. > > For imrotate(eye(...), ..., ...) I just verified that for all of > "nearest", "bilinear" and "bicubic", Matlab's (6.5) and my code > produce *exactly* the same result, modulo the following: > > - My code crops the image a little more aggressively than Matlab > does. I round the size such that 90.1 and 90 degrees (e.g.) give the > same result. Matlab uses the ceil. It's a matter of taste. > > Moreover, Matlab appears to pad the image with zeros, while I do not > pad. Thus, Matlab fills in more values/produces a larger > image. However, I do not see why we should pad; the value of zero is > about as arbitrary as any other value. My code fills in (the > arbitrary value of) zero wherever the result is not defined. Again, > it's a matter of taste, but my method yields simpler code. > > - Both Matlab and my code may produce an odd-size result image for an > even-size source image, and vice versa. In those cases where > Matlab's and my code produce resulting image sizes of opposing > parity, the results are obviously not identical, but neither is more > "correct" than the other. > > Justus > > --=20 > Justus H. Piater, Ph.D. =20 > http://www.montefiore.ulg.ac.be/~piater/ > Institut Montefiore, B28 Phone: +32-4-366-2279 > Universit=E9 de Li=E8ge, Belgium Fax: +32-4-366-2620 > |
From: Justus P. <Jus...@UL...> - 2005-02-25 08:05:15
|
Paul Kienzle <pki...@us...> wrote on Tue, 22 Feb 2005 07:13:43 -0500: > Justus, > > I was testing the wrong code. When I look at it now it looks fine. > Sorry to trouble you. Frankly, it did trouble me ("imrotate seems to be broken for all of...", "particularly glaring", "Can the [other!?] users of this function please come up with a fix"). But I accept your apology. > I would still like you to tell me which tests you prefer. I know I've > thrown a lot of suggestions your way :-) I went through all of the suggestions and cast comprehensive versions of the essence of them into the following three tests, which I suggest to be added to the end of imrotate.m. Justus %!test %! ## Verify minimal loss across six rotations that add up to 360 +/- 1 deg= .: %! methods =3D { "nearest", "bilinear", "bicubic", "Fourier" }; %! angles =3D [ 59 60 61 ]; %! tolerances =3D [ 7.4 8.5 8.6 # nearest %! 3.5 3.1 3.5 # bilinear %! 2.7 0.1 2.7 # bicubic %! 2.7 1.6 2.8 ]; # Fourier %! x =3D peaks(50); %! x -=3D min(min(x)); # Fourier does not handle neg. values well %! for m =3D 1:(length(methods)) %! y =3D x; %! for i =3D 1:5 %! y =3D imrotate(y, 60, methods(m), "crop"); %! end %! for a =3D 1:(length(angles)) %! assert(norm((x - imrotate(y, angles(a), methods(m), "crop")) %! (10:40, 10:40)) < tolerances(m,a)); %! end %! end %!test %! ## Verify exactness of near-90 and 90-degree rotations: %! X =3D rand(99); %! for angle =3D [90 180 270] %! for da =3D [-0.1 0.1] %! Y =3D imrotate(X, angle + da , "nearest"); %! Z =3D imrotate(Y, -(angle + da), "nearest"); %! assert(norm(X - Z) =3D=3D 0); # exact zero-sum rotation %! assert(norm(Y - imrotate(X, angle, "nearest")) =3D=3D 0); # near zer= o-sum %! end %! end %!test %! ## Verify preserved pixel density: %! methods =3D { "nearest", "bilinear", "bicubic", "Fourier" }; %! ## This test does not seem to do justice to the Fourier method...: %! tolerances =3D [ 4 2.2 2.0 209 ]; %! range =3D 3:9:100; %! for m =3D 1:(length(methods)) %! t =3D []; %! for n =3D range %! t(end + 1) =3D sum(imrotate(eye(n), 20, methods(m))(:)); %! end %! assert(t, range, tolerances(m)); %! end --=20 Justus H. Piater, Ph.D. http://www.montefiore.ulg.ac.be/~piater/ Institut Montefiore, B28 Phone: +32-4-366-2279 Universit=E9 de Li=E8ge, Belgium Fax: +32-4-366-2620 |
From: Paul K. <pki...@us...> - 2005-02-25 12:36:54
|
On Feb 24, 2005, at 2:24 PM, Justus Piater wrote: > >> I would still like you to tell me which tests you prefer. I know I've >> thrown a lot of suggestions your way :-) > > I went through all of the suggestions and cast comprehensive versions > of the essence of them into the following three tests, which I suggest > to be added to the end of imrotate.m. > Justus, I've added these tests. Thanks, - Paul |
From: Justus P. <Jus...@UL...> - 2005-02-16 10:08:58
|
> I would appreciate a few simple test cases which do not > rely on a human visual system to determine if the result > is correct. > > I tried the following: > > X =3D rand(30); > Y =3D imrotate(imrotate(X,30,'bicubic'),-30,'bicubic'); > norm(X(17+[1:5],17+[1:5])-Y(30+[1:5],30+[1:5])) > ans =3D 0.46103 Yes, for random images there's no hope... > Using a smoother function worked better: > > X =3D peaks(30); > Y =3D imrotate(imrotate(X,30,'bicubic'),-30,'bicubic'); > norm(X(17+[1:5],17+[1:5])-Y(30+[1:5],30+[1:5])) > ans =3D 0.0036647 I like this. I suggest to use "crop", this saves indexing adjustments and makes the code more transparent and robust to future changes to the size of the rotated image. Moreover, Fourier does not seem to handle negative pixel values: X =3D peaks(30); X -=3D min(min(X)); Y =3D imrotate(imrotate(X, 30, "bicubic", "crop"), -30, "bicubic", "crop"= ); norm(X(10:20,10:20) - Y(10:20,10:20)) ans =3D 0.076178 This can now be used with all four interpolation methods. As a complementary test of the "nearest" method, I suggest to compare near-90-degree rotations to exact 90-degree rotations (only the latter are handled by rot90, with greetings to Todd!). For odd image sizes, the result should be exact: X =3D rand(99); Y =3D imrotate(X, 89.9, "nearest"); Z =3D imrotate(Y, -89.9, "nearest"); norm(X-Z) ans =3D 0 norm(Y - imrotate(X, 90, "nearest")) ans =3D 0 Also try 90.1 degrees, and likewise around 180 and 270. I think it would be useful to add these test cases to imrotate.m. Justus --=20 Justus H. Piater, Ph.D. http://www.montefiore.ulg.ac.be/~piater/ Institut Montefiore, B28 Phone: +32-4-366-2279 Universit=E9 de Li=E8ge, Belgium Fax: +32-4-366-2620 |
From: Paul K. <pki...@us...> - 2005-02-18 04:43:20
|
On Feb 16, 2005, at 5:08 AM, Justus Piater wrote: >> Using a smoother function worked better: >> >> X = peaks(30); >> Y = imrotate(imrotate(X,30,'bicubic'),-30,'bicubic'); >> norm(X(17+[1:5],17+[1:5])-Y(30+[1:5],30+[1:5])) >> ans = 0.0036647 > > I like this. I suggest to use "crop", this saves indexing adjustments > and makes the code more transparent and robust to future changes to > the size of the rotated image. Moreover, Fourier does not seem to > handle negative pixel values: > > X = peaks(30); > X -= min(min(X)); > Y = imrotate(imrotate(X, 30, "bicubic", "crop"), -30, "bicubic", > "crop"); > norm(X(10:20,10:20) - Y(10:20,10:20)) > ans = 0.076178 > > This can now be used with all four interpolation methods. This still begs the question of what is a 'good' value. How about the following: x=peaks(50); y=x; for i=1:5, y=imrotate(y,60,'bicubic','crop'); end norm((x-imrotate(y,59,'bicubic','crop'))(10:40,10:40)) ans = 2.6138 norm((x-imrotate(y,60,'bicubic','crop'))(10:40,10:40)) ans = 0.069348 norm((x-imrotate(y,61,'bicubic','crop'))(10:40,10:40)) ans = 2.6850 This should be a pretty good test of slight over- or under- rotation. I'm sure you already know the rotation is lossy: y=x=peaks(50); for i=1:6, y=imrotate(y,60,'bicubic','crop'); end norm((x-y)(10:40,10:40)) ans = 0.069348 for i=1:6, y=imrotate(y,60,'bicubic','crop'); end norm((x-y)(10:40,10:40)) ans = 0.13736 > As a complementary test of the "nearest" method, I suggest to compare > near-90-degree rotations to exact 90-degree rotations (only the latter > are handled by rot90, with greetings to Todd!). For odd image sizes, > the result should be exact: > > X = rand(99); > Y = imrotate(X, 89.9, "nearest"); > Z = imrotate(Y, -89.9, "nearest"); > norm(X-Z) > ans = 0 > norm(Y - imrotate(X, 90, "nearest")) > ans = 0 > > Also try 90.1 degrees, and likewise around 180 and 270. > > I think it would be useful to add these test cases to imrotate.m. > Send me a patch and I will add it. Embedded tests are run automatically. E.g., I added the following test: %!test %! X = rand(19); %! Z = imrotate(imrotate(X, 89.9, "nearest"), -89.9, "nearest"); %! assert(norm(X-Z),0); See 'help test' for more details. Thanks, - Paul |