0

I am very new to rust coming from Python and some of the concepts and ways to play with the arrays are still very obscure for me. So as an initial exercise I tried to simply open a DICOM stack of images and convert them to grayscale.

I came up with this code following the documentation from dicom and ndarray crates:

fn load_dicom(dcm_path: &str) {
    let dcm = open_file(dcm_path).unwrap();
    let pixel_data = dcm.decode_pixel_data().unwrap();
    let dcm_array = pixel_data.to_ndarray::<f32>().unwrap();

    // FIRST METHOD CHANGING ALL PIXELS:
    let mut grey_array = Array3::<u16>::zeros((
        dcm_array.shape()[0],
        dcm_array.shape()[1],
        dcm_array.shape()[2],
    ));
    let mut grey: f32 = 0.0;
    for fdx in 0..grey_array.shape()[0] {
        for rdx in 0..grey_array.shape()[1] {
            for cdx in 0..grey_array.shape()[2] {
                grey = 0.2989 * dcm_array[[fdx, rdx, cdx, 0]] as f32
                    + 0.5870 * dcm_array[[fdx, rdx, cdx, 1]] as f32
                    + 0.1140 * dcm_array[[fdx, rdx, cdx, 2]] as f32;
                grey_array[[fdx, rdx, cdx]] = grey.round() as u16;
            }
        }
    }
}

This method works but it's VERY slow, which is probably not too surprising given that it changes every pixel one by one through all the images of the DICOM image stack.

Now from that I tried to use already built functions such as some from the image crate:

fn load_dicom(dcm_path: &str) {
    let dcm = open_file(dcm_path).unwrap();
    let pixel_data = dcm.decode_pixel_data().unwrap();
    let mut vec = Vec::new();
    for fdx in 0..dcm_array.shape()[0] {
        let img = pixel_data.to_dynamic_image(fdx as u32).unwrap();
        vec.push(img.grayscale());

But this one gives me a flat array which is not what I want (I want to be able to access and play with every frame separately)

Does anyone have an idea?

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
Unic0
  • 341
  • 1
  • 3
  • 19
  • 1
    _"very slow"_ → did you try it in release mode? Note that in Python, functions that operate on the whole image at once still need to access each pixel one by one internally. The reason they are faster than doing the same in Python is because they are compiled to machine code, whereas Python is interpreted. Since Rust is compiled too, it can access individual pixels as fast as the equivalent global functions. – Jmb Jun 03 '22 at 07:55
  • Indeed, I did you use `cargo build --release` followed by checking the execution time `../target/release/project`. Thank you for that ! – Unic0 Jun 04 '22 at 05:33
  • You could try with [`map_axis`](https://docs.rs/ndarray/latest/ndarray/struct.ArrayBase.html#method.map_axis), e.g. something like: `let grey_array = dcm_array.map_axis (Axis (3), |pix| { (0.2989 * pix[0] as f32 + 0.5870 * pix[1] as f32 + 0.1140 * pix[2] as f32).round() as u16 })` – Jmb Jun 07 '22 at 06:45

0 Answers0