(1, 1)
doesn't stay there which would be arguably unexpected for changing dimensions. So I'd rather add it only with stronger motivation.
ImageBuffer
.
/// swaps width and height for when transposing image
pub fn swap_dimensions(&mut self) {
let (width, height) = self.dimensions();
self.width = height;
self.height = width;
}
I implement this function for ImageBuffer
, in the buffer file, which will swap the dimensions, then in my function:
pub fn box_filter_mut(filter: MeanKernel, mut image: &mut RgbaImage) {
use crate::matrix_ops::transpose_rgba;
use image::Pixel;
let (width, height) = image.dimensions();
// want the truncated value of this division, hence not using float
let radius: i32 = filter.size() as i32 / 2;
let mut new_image: RgbaImage =
ImageBuffer::from_pixel(width, height, Rgba::from_channels(255, 255, 255, 255));
// blur pixels row wise
horizontal_blur(radius, &image, &mut new_image);
// swap image dimensions to allow transposing new_image into image
image.swap_dimensions();
transpose_rgba(&new_image, &mut image);
// swap new_image dimensions to allow blurring image and writing
// to new_image
new_image.swap_dimensions();
// blur pixels column wise
horizontal_blur(radius, &image, &mut new_image);
// swap dimensions again to transpose new image into image for final
// output
image.swap_dimensions();
transpose_rgba(&new_image, &mut image);
}
I'm not necessarily asking for this to be implemented, but I want to clarify what I'm trying to do. The reason why i'm trying to reuse buffers is because I'm compiling this to Webassembly and I'm trying to cut down on allocations. I'm using WeeAlloc as the allocator.
/// sets dimensions of image
pub fn set_dimensions(&mut self, width: u32, height: u32) -> Result<(), String> {
if (width * height * <P as Pixel>::CHANNEL_COUNT as u32) as usize > self.data.len() {
return Err("width and height too large for container".to_string());
}
self.width = width;
self.height = height;
Ok(())
}
something like this would be nice too. I don't know how useful this would be to the overall project though.
ConvertInplace
interface proposed in this issue could even be used to take an image and convert into a transposed image, if that makes sense.
It seems like an separate isse. ConvertInplace was, to my understanding of the draft, meant to preserve pixel relation and also work pixel-by-pixel. This enables some parallelism where a true transpose of the image matrix requires a different kind of parallelization strategy. Your implemtation of swap_dimensions
of course only perserves semantics together with transpose_rgba
. I would recommend going to flat::Flat
for this purpose, it gives you full control over all aspects.
The reason why i'm trying to reuse buffers is because I'm compiling this to Webassembly and I'm trying to cut down on allocations. I'm using WeeAlloc as the allocator.
Please look closely at the code here, and you will find that it does not allocate any memory. Yet, it gives you an intermediate owned Vec of the pixel data. You seem to be under the impression that &mut RgbaImage
makes this impossible. That is not the case. The Rust type system only requires you to give a temporary replacement, which can be constructed without an allocation by substituting a zero-sized image.
let temp = core::mem::replace(image, ImageBuffer::new(0, 0)); let temp: RgbaImage = ImageBuffer::from_raw(height, width, tmp.into_raw()).unwrap(); *image = temp;
@ruffson: Great, if you open an issue, I'll also add my reproduction to it :)
Cool, I'll ping you when I did it.
Limits
struct in the TIFF decoder mod.rs
has a private field _non_exhaustive
which means I cannot simply create a new struct with Limits{...}
, what is the idiomatic way of creating a new Limits
struct outside of this module if you want to change e.g. all three default values? Creating a struct with Limits::default()
and then changing each field one by one?
Interesting. So the examples are all too long?
That's weird but maybe not too bad.. What would happen if we were to truncate?
This may have previously worked incidentally.
while bytes_read < compressed_length && uncompressed.len() < max_uncompressed_length {
let (len, bytes) = decoder.decode_bytes(&compressed[bytes_read..])?;
uncompressed.extend_from_slice(bytes);
Since the lzw
library decodes symbols word-for-word it's not unlikely that we hit exactly the right length.
The new library decodes fixed size chunks
buffer.byte_len
?
According to the docs: https://developer.gnome.org/gdk-pixbuf/unstable/gdk-pixbuf-The-GdkPixbuf-Structure.html#image-data
Image data in a pixbuf is stored in memory in uncompressed, packed format. Rows in the image are stored top to bottom, and in each row pixels are stored from left to right. There may be padding at the end of a row. The "rowstride" value of a pixbuf, as returned by gdk_pixbuf_get_rowstride(), indicates the number of bytes between rows.
Hi, I got stuck a bit when trying to implement a combined transformation...
How do I get from a GenericImageView
to an ImageBuffer
(without any modification) without iterating over rows/cols and calling get_pixel / set_pixel? (That seems really inefficient to me)
Also, is there a good way to combine multiple transforms efficiently without copying each time?
For context, I'm implementing EXIF rotation. This means that depending on the input parameter, we'll do nothing, rotate, flip or rotate + flip.
Rotating by 0 and 180 degrees as well as flipping can be done in-place, so no copying would be necessary. The buffer could be modified in-place.
Rotating by 90 and 270 degrees however always requires a copy, because the image may not be square.
Right now the APIs seem to take &self
and return a new ImageBuffer
. Do you have a good way to solve this? We could use something like fn rotate(&mut self) -> RotationResult
with enum RotationResult { Modified, Copied<ImageBuffer> }
, but that seems very ugly from an API point of view.
image
itself anways.GenericImage
-based interface is not at all optimized for this work. For example, as you've observed, it isn't efficient to iterate pixels individually but that's pretty much all you can do with the generic interface and what's done internally. Also the layout would—I guess—be more efficient (cache oblivious) if it were a recursive, space-filling curve of macroblocks of pixels instead of row-by-row or col-by-col but that requires an extra conversion of layouts and is somewhat specialized.image
currently.