[Rust] image-rs: 이미지 처리 도구

image-rs는 Rust로만 구현된 이미리 처리 라이브러리다.
이미지 포맷도 꽤 다양하고, 개중에서 가장 많이 사용되는 편인 것 같다.
저수준제어에 특화되진 않았고, GPU 쥐어짜는 구조도 아니라서 성능 최적화에 한계는 있을 것이다.

지원되는 포맷은 다음과 같다.




설치

설치는 매우 간단하다. image라는 이름의 단일 디펜던시만 추가하면 된다.




기본 사용법

한번 jpg 이미지 하나를 가져다가 간단한 가공을 해보겠다.

이걸 png 포맷의 이미지로 교체해보자.

아래는 그에 대한 간단한 코드다.

use image::GenericImageView;

fn main() {
    // Use the open function to load an image from a Path.
    // `open` returns a `DynamicImage` on success.
    let img = image::open("bonobono.jpg").unwrap();

    // The dimensions method returns the images width and height.
    println!("dimensions {:?}", img.dimensions());

    // The color method returns the image's `ColorType`.
    println!("{:?}", img.color());

    // Write the contents of this image to the Writer in PNG format.
    img.save("test.png").unwrap();
}

컴파일해서 실행해보면


png로 바뀌어있을 것이다.
저 save 함수는 파일 확장자를 기반으로 확장자를 결정하는데, 그게 싫다면 save_with_format으로 이미지 포맷을 명시할 수도 있다.


이렇게 말이다.
이 코드는 이미지를 WebP로 저장한다.




돌리기

rotate 함수를 사용하면 이미지를 바로 회전한 채의 복사본을 반환한다.




리사이즈

리사이즈 기능도 제공한다.
만약 가장 큰 쪽을 기준으로 해서 300 사이즈로 줄이고 싶다면, 이렇게 사용할 수 있다.

그럼 긴 쪽이 300으로 맞춰서 줄어든 것을 볼 수 있다. 이 함수는 항상 비율을 유지한다.


실제로도 작게 잘 줄었다.

이미지 비율을 유지할 필요가 없다면 resize_exact 함수를 사용할 수 있다.

이건 무조건 크기에 맞춰서 깎아버리기 때문에


이렇게 빠그라질 수 있다.

반면 resize_to_fill은 이미지를 구겨넣는게 아니라 자르는 동작을 한다.

사이즈는 지정한대로 쫙 끼는 것을 볼 수 있다.


사이즈도 언급했듯이 강제로 잘린다.




썸네일

썸네일 기능은 resize와 동작상 비슷하나, 더 빠르게 최적화된다는 특징이 있다.
그대신 anti aliasing이라고 부르는 화질 저하가 더 심하다.

사용법은 resize과 거의 동등하다. 썸네일처럼 화질이 구려도 되는 경우에만 사용하면 된다.




자르기

crop 함수를 사용하면 임의로 이미지를 잘라서 반환할 수 있다.
이 함수는 특이하게 또 mutable이다.

이 코드는 30, 30 좌표에서 시작해 200,200 크기만큼 자른다는 뜻이다.

저대로 컴파일해보면

잘 잘릴 것이다.




선명도 조절: blur

blur 함수를 사용하면 이미지의 선명도도 조작할 수 있다.

그럼 이렇게 부옇게 뜬다.




밝기 조절

밝기 조절도 된다. brighten 함수를 사용하면 된다.

이런 기본적인 기능들 외에도, image-rs는 sub-crate인 imageproc을 통해 고급화된 이미지 처리 옵션을 제공한다.
https://docs.rs/imageproc/latest/imageproc/
더 많은걸 원한다면 참고하면 된다.





이미지 버퍼로 직접 이미지 생성하기

기존 이미지를 가져다가 가공하고 조작하는 용도로 쓸 수도 있지만, 직접 이미지를 직조해내는 기능도 제공한다.
아래는 이미지 버퍼를 하나 깔고 거기다가 그림을 그리는 코드다.

fn main() {
    let imgx = 800;
    let imgy = 800;

    let scalex = 3.0 / imgx as f32;
    let scaley = 3.0 / imgy as f32;

    // Create a new ImgBuf with width: imgx and height: imgy
    let mut imgbuf = image::ImageBuffer::new(imgx, imgy);

    // Iterate over the coordinates and pixels of the image
    for (x, y, pixel) in imgbuf.enumerate_pixels_mut() {
        let r = (0.3 * x as f32) as u8;
        let b = (0.3 * y as f32) as u8;
        *pixel = image::Rgb([r, 0, b]);
    }

    // A redundant loop to demonstrate reading image data
    for x in 0..imgx {
        for y in 0..imgy {
            let cx = y as f32 * scalex - 1.5;
            let cy = x as f32 * scaley - 1.5;

            let c = num_complex::Complex::new(-0.4, 0.6);
            let mut z = num_complex::Complex::new(cx, cy);

            let mut i = 0;
            while i < 255 && z.norm() <= 2.0 {
                z = z * z + c;
                i += 1;
            }

            let pixel = imgbuf.get_pixel_mut(x, y);
            let image::Rgb(data) = *pixel;
            *pixel = image::Rgb([data[0], i as u8, data[2]]);
        }
    }

    // Save the image as “fractal.png”, the format is deduced from the path
    imgbuf.save("fractal.png").unwrap();
}

그럼 이렇게 그려진다.

저수준 제어도 가능은 하다.



참조
https://github.com/image-rs/image