Coder Social home page Coder Social logo

kornelski / imgref Goto Github PK

View Code? Open in Web Editor NEW
57.0 4.0 6.0 172 KB

A trivial Rust struct for interchange of pixel buffers with width, height & stride

Home Page: https://lib.rs/crates/imgref

License: Creative Commons Zero v1.0 Universal

Rust 100.00%
rust-library image pixel-layout

imgref's Introduction

2D slice of a an image

This is a minimal struct that represents a 2-dimensional vector and rectangular slices of it. It's intended for describing image data, and regions of images (AKA bitmap descriptors, frames with a pitch or stride, planes). It's useful when working with image data, and can be used as the common struct for exchanging image pixels between crates.

In graphics code it's very common to pass width and height along with a Vec or a slice of pixels — all as separate arguments. This gets very repetitive, and can lead to errors.

Additionally, some graphics code requires use of stride, which allows defining sub-regions of images without copying. This is useful for slicing and tiling, and to support bitmaps that require padding (e.g. in video formats that round frame sizes to a multiple of a block size).

This crate is a simple struct that adds dimensions to the underlying pixel buffer. This makes it easier to correctly keep track of the image size and allows passing images with just one function argument instead three or four. For convenience, it also implements efficient iterators for pixels/rows and indexing with img[(x,y)].

use imgref::*;

fn main() {
    let img = Img::new(vec![0; 1000], 50, 20); // 1000 pixels of a 50×20 image

    let new_image = some_image_processing_function(img.as_ref()); // Use imgvec.as_ref() instead of &imgvec for better efficiency

    println!("New size is {}×{}", new_image.width(), new_image.height());
    println!("And the top left pixel is {:?}", new_image[(0u32,0u32)]);

    let first_row_slice = &new_image[0];

    for row in new_image.rows() {}
    for px in new_image.pixels() {}

    // slice (x, y, width, height) by reference - no copy!
    let fragment = img.sub_image(5, 5, 15, 15);

    // create a vec of pixels without stride, for compatibility with software
    // that expects pixels without any "gaps"
    let (vec, width, height) = fragment.to_contiguous_buf();
}

Type aliases

Illustration: stride is width of the whole buffer.

These are described in more detail in the reference.

ImgVec

It owns its pixels (held in a Vec). It's analogous to a 2-dimensional Vec. Use this type to create and return new images from functions.

Don't use &ImgVec. Instead call ImgVec.as_ref() to get a reference (ImgRef) from it (explicit .as_ref() call is required, because Rust doesn't support custom conversions yet.)

ImgRef

ImgRef is a reference to pixels owned by some other ImgVec or a slice. It's analogous to a 2-dimensional &[].

Use this type to accept read-only images as arguments in functions. Note that ImgRef is a Copy type. Pass ImgRef, and not &ImgRef.

Requirements

  • Latest stable Rust (1.42+)

imgref's People

Contributors

kornelski avatar kpreid avatar tim77 avatar yoanlcq avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

imgref's Issues

please consider relicensing this project - use of CC0 for source code is now discouraged

The legal status / acceptance of the CC0-1.0 license has recently changed. It is now considered to not be a "good" license for Open Source code due to the lack of an explicit patent waiver.

The application for CC0-1.0 to become an OSI-approved license was withdrawn after receiving criticism, and it is now officially no longer considered an acceptable license for source code for projects that are packaged for Fedora Linux.

Historically, many other projects have followed the Fedora Project's lead on issues like this, so I expect CC0-1.0 to soon lose its status as an acceptable FOSS license across the ecosystem. For more background, there's an article on The Register.


EDIT: Alternatives to CC0-1.0 might be the Unlicense (both OSI-approved and FSF-libre, also a Public Domain dedication, similar to CC0-1.0), MIT-0 (OSI-approved, but not FSF-libre), or 0BSD (OSI-approved, but not FSF-libre).

Add accessor for continuous buffer

Interoperability with other libraries may require getting the underlying buffer as a slice or vec, but entirely continuous (where width == stride).

How can I implement a trait for `imgref::Img<std::vec::Vec<i32>>`

I'm trying to use inline-python to get a pic from OpenCV camera, then manipulate this image with Rust, so I wrote the below code that catch the photo correctly, but once I try to handle it in Rust using imgref I get the below error:

error[E0277]: the trait bound `imgref::Img<std::vec::Vec<i32>>: pyo3::pyclass::PyClass` is not satisfied
  --> src\main.rs:44:44
   |
44 |                 let img: Img<Vec<i32>> = c.get("frame");
   |                                            ^^^ the trait `pyo3::pyclass::PyClass` is not implemented for `imgref::Img<std::vec::Vec<i32>>`
   |
   = note: required because of the requirements on the impl of `for<'p> pyo3::conversion::FromPyObject<'p>` for `imgref::Img<std::vec::Vec<i32>>`

The full code is:

#![feature(proc_macro_hygiene)]
use inline_python::*;

use imgref::*;
use web_view::*;
 
fn main() {
    let c = Context::new();

    web_view::builder()
    .title("Call open CV")
    .content(Content::Html(HTML))
    .size(800, 600)
    .user_data(())
    .invoke_handler(|_, arg| {
        match arg {
            "open" => {
                c.run(python!  {
                            import cv2
                            x = 2
                            cap = cv2.VideoCapture(0)
                            ret, frame = cap.read()
                            cv2.imshow('frame', frame)
                            cv2.waitKey(0)
                    })
            },
            "close" => {
            
                let img: Img<Vec<i32>> = c.get("frame");

            },
            _ => (),
        }
        Ok(())
    }).run()
    .unwrap();
}

const HTML: &str  = r#"
<!doctype html>
<html>
    <body>
        <button onclick="external.invoke('open')">Run</button>
        <button onclick="external.invoke('close')">Close</button><br>
        <br>

    </body>
</html>
"#;

Can you help, Thanks.

How can I build image from 3D array

I've the content of an image extracted in a 3D array, with the dimensions of: (480, 640, 3), which can be built in Rust with one of the below ways, how can I convert this array into image?
image

Image raw data:

fn main() {
  // let mut vec: Vec<Vec<Vec<i32>>> = Vec::with_capacity(j * i * k);
  // println!("Vector size = {}, {}, {}", vec.len(), vec[0].len(), vec[0][0].len()); 
   let j = 3;
   let i = 640;
   let k = 480;
   let raw_data = vec![vec![vec![0i32; j]; i]; k];
   println!("{:?}", raw_data);
}

Or as:

use ndarray::prelude::*;
use ndarray::Array;
fn main() {
  let raw_data = Array::<i32, _>::zeros((480, 640, 3).f());
  println!("{:?}", raw_data);
}

I started with below code, but not sure where to go next:

fn rust_print(vec: Vec<Vec<Vec<i32>>>) {
    println!("Vector size = {}, {}, {}", vec.len(), vec[0].len(), vec[0][0].len());    // Vector size = 480, 640, 3
    let imgvec = Img::new(vec![0; vec.len() * vec[0].len()],
                       vec.len(), vec[0].len());
    println!("New size is {}×{}", imgvec.width(), imgvec.height());   // New size is 480×640
    for px in imgvec.pixels() {
    }
}

With content:

[[[151 134 128]
  [148 131 126]
  [149 130 127]
  ...
  [225 193 182]
  [222 192 181]
  [223 193 182]]

 [[152 132 129]
  [149 130 127]
  [151 131 128]
  ...
  [222 192 181]
  [222 192 181]
  [223 193 182]]

 [[152 132 129]
  [152 132 129]
  [150 132 129]
  ...
  [220 192 181]
  [222 192 183]
  [223 193 184]]

 ...

 [[ 88  87  92]
  [ 90  89  93]
  [ 88  87  94]
  ...
  [ 70  66 105]
  [ 69  65 104]
  [ 70  66 105]]

 [[ 88  87  92]
  [ 88  87  92]
  [ 87  86  91]
  ...
  [ 68  65 109]
  [ 69  65 104]
  [ 70  66 105]]

 [[ 87  87  89]
  [ 87  87  89]
  [ 86  85  90]
  ...
  [ 67  65 104]
  [ 69  65 104]
  [ 71  67 106]]]
``

Limitation of pixel-sized instead of byte-sized `stride`.

I recently implemented a Rust wrapper for my zxing-cpp barcode scanning library. On the c++ side I have the ImageView class that I needed to wrap/replace in Rust. I thought about using ImgRef for that but came to the conclusion that the pixel-sized stride implementation is not general enough. You explicitly mention video frame buffers as a potential input but it seems to me your decision to define the stride as a factor for sizeof(Pixel) does prohibit that use case in general.

Say you have an RGB frame buffer (3 bytes per pixel) of size 2x2 but your video hardware insist to start each row on a 4-byte aligned address. So the first row starts at address p : *const u8, then the next starts at p + 8 but you can't specify that as an ImgRef.

I understand that this is a consequence of defining your underlying container as a series of pixels instead of u8 data. Do you know of an alternative to ImgRef that allows that? Is this use-case so niche that no-one needs it anyway? I believe the QImage class from qt also has this internal memory layout.

Why are the fields public?

After all the assertions in the code, it's weird that the fields are public, since anyone could change them right afterwards, placing the object in an invalid state. Maybe make it harder for people to shoot themselves in the foot by using private fields and getters (and maybe setters, if necessary)?

Why must width and height be non-zero?

Currently, width and height are not allowed to be zero. In a way, this makes sense, but IMHO it is surprising and impractical.

  • AFAIK this isn't mentioned in the documentation, so this is something that might bite at runtime. In any case, if in the end we decide to preserve the current behavior, then it would be nice to provide a rationale in the docs;
  • It is not an error for a Vec or slice to be empty; In fact this is true of all collections that I know of, and it's quite convenient. Images are "different" because there's no such thing as a 0x0 image; However, there is such a thing as an empty 2D container; For instance...
  • I was building a font atlas by rasterizing each ASCII character I was interested in via FreeType into individual bitmaps, then copying the bitmaps into the atlas.
    I decided to change my API so that it returns glyph bitmaps as ImgVecs for convenience, but suddenly I get a panic; turns out, the ' ' (space) character effectively yields a 0x0 bitmap.
    My issue is that it is was actually fine, and the code should have kept running as normal. Now I have to check that the height is non-zero every time, and my API now returns Option<ImgVec<...>> instead of just ImgVec<...>, introducing some noise and branching (but these are nitpicks).
    Of course the issue was not hard to find and it's not going to show up anytime soon. The point is that my code worked fine until I brought imgref in... x)

This kind of decision is inherently opinionated and I don't wish to enforce my point of view. It might also be interesting to see what's the stance of similar crates on the matter. :)

Why does index/index_mut require Pixel to implement Copy

The following code does not compile because String does not implement Copy:

fn main() {
    let vec = imgref::ImgVec::new(vec!["world!`".to_string()], 1, 1);
    println!("Hello, {}", vec[(0usize, 0usize)]);
}

If you look at imgref/ops.rs it requires Pixel to implement Copy.
Why is Copy required?

Alternative to Vec as backing store?

Using SIMD instructions on windows requires allocations be aligned to 16 bytes. Vec does not align to 16 bytes when allocating.

Vec on stable does not permit fallible allocations either, which is critical when allocating large bitmaps.

Would it be possible/practical to allow something other than Vec as a container, or should this be a separate crate?

Trait for bitmaps with arbitrary impl

Currently the struct is tied to a particular layout/implementation and doesn't support:

  • upside-down bitmaps (negative stride)
  • byte-based stride that isn't multiple of full pixels (e.g. 2-byte gaps in RGBA bitmaps)

So perhaps this should be a trait first and foremost.

How to copy pixels from one image to another?

Let's say I need to extract an alpha channel from an image. I've ended up with code like this:

let mut image1 = ImgVec::new(vec![RGBA8::new(0, 0, 0, 0); 4], 2, 2);
let mut image2 = ImgVec::new(vec![0; 4], 2, 2);
for (x, row) in image1.rows().enumerate() {
    for (y, p) in row.iter().enumerate() {
        image2[(x, y)] = p.a;
    }
}

It this the only way or am I missing something?

Interop with the "image" crate

I'm using the image crate to load images, and I was wondering: is there a method to easily and quickly convert a loaded RgbaImage into an Img? Right now I'm doing it manually like this:

    pub fn image_to_imgref(img: &RgbaImage) -> Img<Vec<rgb::RGBA<u8>>> {
        Img::new(
            img.pixels()
                .map(|p| rgb::RGBA::new(p.0[0], p.0[1], p.0[2], p.0[3]))
                .collect::<Vec<_>>(),
            img.width() as usize,
            img.height() as usize,
        )
    }

I was wondering if it is possible to do this in a one-liner. I was also wondering if it was possible to do it the other way around...

rayon

Would it be possible to add rayon support? Right now par_bridge can't be used because the iterators aren't Send.

`Img::map_buf_unchecked`

I'm doing unsafe shenanigans:

	let buf_ptr = Img::new_stride(*buffer.buf() as *const [T], buffer.width(), buffer.height(), buffer.stride());

I promise this is for a good reason (efficiently iterating over the columns of a buffer). I see Img::map_buf exists, but it requires the new buffer to be AsRef, which pointers are not.

I'd appreciate an unsafe function map_buf_unchecked that doesn't need to do any validation checks.

Add `copy_sub_image()` based on `ptr::copy_nonoverlapping()`

This is doable with the current API using row iterators, but it's slightly annoying. I'd like a single function that does this. My use case is building font atlases from indiviual bitmaps.

I figure this would (roughly) look like this:

impl<'a, Pixel: Copy> ImgRefMut<'a, Pixel> {
    pub fn copy_sub_image(&mut self, dst_left: usize, dst_top: usize, src: ImgRef<Pixel>, src_left: usize, src_top: usize, width: usize, height: usize) {
        self.sub_image_mut(dst_left, dst_top, width, height).copy_image(src.sub_image(src_left, src_top, width, height))
    }
    pub fn copy_image(&mut self, src: ImgRef<Pixel>) {
        assert_eq!(self.width(), src.width());
        assert_eq!(self.height(), src.height());
        // Then, use row iterators and `ptr::copy_nonoverlapping()` to perform the copy efficiently.
    }
}

(The API is inspired by GDI's BitBlt()).

Simple way to iterate over all pixels of an image, with their coordinates

I sometimes need to iterate over all pixels of an image and do some operation depending on their coordinates.

I typically want to do this mutably, which I can do like this:

    for (y, row) in image.rows_mut().enumerate() {
        for (x, pixel) in row.iter_mut().enumerate() {
            *pixel = shade_pixel(x, y);
        }
    }

But this requires a nested loop and hence an additional level of indentation, and a bit too much code for my taste. Wouldn't it be neat with an iterator that yields (x, y, pixel) instead, that could be used like this:

    for (x, y, pixel) in image.enumerate_pixels_mut() {
        *pixel = shade_pixel(x, y);
    }

I used the name enumerate_pixels_mut, which should have a matching enumerate_pixels for immutable iteration.

Is it a good idea, or can be done neatly with the functionality that already exists?

Calling `to_owned` on `ImgRef` returns another `ImgRef` intead of `ImgVec`

Reproducer:

fn  foo(img: ImgRef<'_, RGB8>) -> ImgVec<RGB8> {
    img.to_owned()
}

The above code will not compile:

   |
95 | fn foo(img: ImgRef<'_, RGB8>) -> ImgVec<RGB8> {
   |                                  ------------ expected `imgref::Img<std::vec::Vec<rgb::RGB<u8>>>` because of return type
96 |     img.to_owned()
   |     ^^^^^^^^^^^^^^ expected `Img<Vec<RGB<u8>>>`, found `Img<&[RGB<u8>]>`
   |
   = note: expected struct `imgref::Img<std::vec::Vec<rgb::RGB<u8>>>`
              found struct `imgref::Img<&[rgb::RGB<u8>]>`

As seen in the error, to_owned had no effect.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.