image-rs / imageproc

Image processing operations
MIT License
744 stars 146 forks source link

Projection matrix for rotation is weird #674

Closed EmiOnGit closed 3 months ago

EmiOnGit commented 3 months ago

First of, thanks for the great crate! :) I've been trying to rotate a image around all 3 axis. The z-axis does as expected but I ran into some weird transformations for the other 2. I tested the same code in python with opencv and it worked.

The minimal example

fn main() {
    let img = ImageReader::open("myimage.jpg")
        .unwrap()
        .decode()
        .unwrap()
        .into_rgb8();
    // Can be changed
    let angle_in_degree = 0.1;
    // from the glam crate but shouldn't be a problem
    let rotation_matrix = Mat3::from_rotation_x(angle_in_degree * PI / 180.).to_cols_array();
    let half_w = img.width() as f32 / 2.;
    let half_h = img.height() as f32 / 2.;
    // go to center -> rotate around axis -> move back
    let projection = Projection::translate(half_w, half_h)
        * Projection::from_matrix(rotation_matrix).unwrap()
        * Projection::translate(-half_w, -half_h);
    // apply the projection
    let new_img = geo_trans::warp(&img, &projection, Interpolation::Nearest, Rgb::black());
    new_img.save("new_img.jpg").unwrap();
}

(The translations in the projections are not needed for the problem but make the problem more obvious in the images)

While this is only a angle of 0.1 degree the image is already way more tilted than it should be:

Using a angle of 1 degree ends up with following image:

A rotation around the z-axis with 20 degree however, seem to very reasonable:

I also noticed that using

    let new_img = geo_trans::warp_with(
        &img,
        |x, y| projection * (x, y),
        Interpolation::Nearest,
        Rgb::black(),
    );

instead of warp() causes the image to rotate in the other direction but I'd guess that's a different issue. Same 20 degree tilt but using warp_with: