I want to put a local image into the clipboard using Rust. I used the clipboard-win and image crates. My code is as follows, but it doesn't work.
extern crate clipboard_win;
extern crate image;
use clipboard_win::{formats, Clipboard};
use image::GenericImageView;
fn main() {
let img = image::open("C:\\Users\\Crash\\Desktop\\20190405221505.png").unwrap();
Clipboard::new()
.unwrap()
.set(formats::CF_BITMAP, &img.raw_pixels());
}
After execution, there seems to be content in the pasteboard, but there is nothing displayed after Ctrl+V. How can I correct this code?
You have multiple problems.
Format
A PNG format image is not a bitmap format image, even though it is a bitmap.
A thread on MSDN states:
There isn't a standardized clipboard format for PNG.
You can register your own format, but then only you can recognize the clipboard. If you use the standard bitmap or file format then more applications can accept your data.
Error handling
Clipboard::set can fail and it returns a Result. You need to handle this case. The compiler even told you about this:
warning: unused `std::result::Result` that must be used
--> src\main.rs:11:5
|
11 | / Clipboard::new()
12 | | .unwrap()
13 | | .set(formats::CF_BITMAP, &data);
| |________________________________________^
|
= note: #[warn(unused_must_use)] on by default
= note: this `Result` may be an `Err` variant, which should be handled
Don't ignore warnings, especially when trying to debug a problem.
Unfortunately, this is as far as I got:
use clipboard_win::{formats, Clipboard}; // 2.1.2
use image::ImageOutputFormat; // 0.21.0
fn main() {
let img = image::open("unicorn.png").unwrap();
let mut data = Vec::new();
img.write_to(&mut data, ImageOutputFormat::BMP)
.expect("Unable to transform");
Clipboard::new()
.unwrap()
.set(formats::CF_BITMAP, &data)
.expect("Unable to set clipboard");
}
Writing data to a file produces a BMP that Paint can read, but the clipboard data is still invalid. While attempting to debug the differences, I ran into low-level crashes in the library, which suggests it may not be ready for general use, despite the 2.x version number.
I believe that the root problem is that
Windows expects
A handle to a bitmap (HBITMAP).
A BITMAP is a struct with a set of information about the bitmap, such as width and height. This is likely different from the on-disk format of the bitmap.
It seems plausible that fitting the bitmap data into this expected format would go a long way to solving the problem.
Another avenue is to look into using CF_DIB instead of CF_BITMAP. Contrary to the linked forum post above, CF_DIB expects a pointer to a BITMAPINFO which has a BITMAPINFOHEADER field. This makes a reference to a BI_PNG compression, which might allow you to submit a PNG without performing transformations.
See also:
How do I encode a Rust Piston image and get the result in memory?
Related
How can I do similar thing but with DynamicImage (the result of example <- readImage "/filepath.png"):
negative :: Image PixelRGBA8 -> Image PixelRGBA8
negative = pixelMap $ \(PixelRGBA8 r g b a) -> PixelRGBA8 (255 - r) (255 - g) (255 - b) a
And how it's working without any arguments despite signature?
I guess that's what I need, because there's no writeJpg function provided that does work with Image (not Dynamic) type.
I know about dynamicPixelMap function provided by JuicyPixels, but I do not actually understand how to work with it. It will be good if someone can explain it.
UPD:
I've found simple solution:
negative :: Image PixelRGBA8 -> Image PixelRGBA8
dynamicImage <- readImage filepath
let image = convertRGBA8 <$> dynamicImage
let modified = negative <$> image
case modified of
Left err -> print err
Right image -> saveJpgImage 100 outputFile $ ImageRGBA8 image
It's an example, my code is more complicated.
Let's start by looking at why DynamicImage exists. Just looking at the definition is sufficient to make that clear: There are lot of different formats for color data in images. The input routines JuicyPixels provides preserve the color data the way it was stored in the file. But the Image type puts the color data format into the type. This is a problem in Haskell - if you make a function polymorphic, the caller gets to choose the concrete type, not the function itself. There's no way to say "this returns some Image type, depending on the input". So DynamicImage exists - it has a different constructor for each supported color data format. Matching on the constructor tells you the type of Image you're working with, so you can't get the data without also knowing what type it is.
Since the constructors are public, you can match on them to get an Image and use pixelMap on it... But I wouldn't. JPEG files are sometimes in CMYK space, not RGB. Logic in that space is entirely different - it's a subtractive space, rather than additive. And JuicyPixels provides tools to help you out here. The convertRGBA8 function takes a DynamicImage and does whatever colorspace conversion is necessary to give you an Image PixelRBGA8. The conversion will lose details in some cases, but it will get you the format in the example you provided.
I am currently trying to make some TensorFlow Inference (C backend) using Boost::GIL (challenging). I need a few thinks, I have been able to load my png image (rgb8_image_t)
and did a conversion to rgb32_f_image_t.
I still need 3 thinks, the raw pointer of the data, memory allocated, and dimensions.
for the memory allocated unfortunately the function total_allocated_size_in_bytes() is private, so I did this:
boost::gil::view(dest).size() * boost::gil::view(dest).num_channels() * sizeof(value_type);
Which is valid, if I do not have any extra padding for alignment story. But does it exist any nice alternative?
For the dimension, I should match with numpy (from PILLOW), I hope both libraries are using the same memory layout pattern. From my understanding, by default, datas are interleaved and contiguous so, it should be good.
Last the raw pointer _memory, it is a private data member of the Image class with no dedicated function. boost::gil::view(dest).row_begin(0) returns a iterator on the first pixel but I not sure how I could get the pointer of the data _memory. Any suggestions ?
Thank you very much,
++t
ps: TensorFlow proposes a C++ backend, however, it is not installed from any package managers, and manipulate Bazel is beyond my strength.
GIL documentation pretty accurately documents the various memory layouts.
The point of the library, though, is to abstract away the memory layouts. If you require some representation (planar/interleaved, packed or unpacked) you are doing things "the hard way" for the library interface.
So, I think you can read and convert in one go, e.g. for a jpeg:
gil::rgb32f_image_t img;
gil::image_read_settings<gil::jpeg_tag> settings;
read_and_convert_image("input.jpg", img, settings);
Now getting the raw data is possible:
auto* raw_data = gil::interleaved_view_get_raw_data(view(img));
It happens to be the case that the preferred implementation storage is interleaved, which is likely what you're expecting. If your particular image storage is planar, the call will not compile (and you'd probably want planar_view_get_raw_data(vw, plane_index) instead).
Note that you'll have to reinterpret_cast to float [const]* if you need that, because there is not public interface to get a reference to the scoped_channel_value<>::value_, but the BaseChannelValue type is indeed float and you can assert that the wrapper doesn't add additional weight:
static_assert(sizeof(float) == sizeof(raw_data[0]));
Alternative Approach:
Conversely, you can setup your own raw pixel buffer, mount a mutable view into it and use that to read/convert your initial load into:
// get dimension
gil::image_read_settings<gil::jpeg_tag> settings;
auto info = gil::read_image_info("input.jpg", settings).get_info();
// setup raw pixel buffer & view
using pixel = gil::rgb32f_pixel_t;
auto data = std::make_unique<pixel[]>(info._width * info._height);
auto vw = gil::interleaved_view(info._width, info._height, data.get(),
info._width * sizeof(pixel));
// load into buffer
read_and_convert_view("input.jpg", vw, settings);
I've actually checked that it works correctly by writing out the resulting view:
//// just for test - doesn't work for 32f, so choose another pixel format
//gil::write_view("output.png", vw, gil::png_tag());
On the LibTIFF documentation, there is no mention or sample of how to read a 16 bit RGB image. Apparently also, using the scanline functions is required for reading a 16 bit. After a few attempts I couldn't get that to work.
According to this post,
If you want to handle all kind of TIFF image wihout using
TIFFReadRGBAImage then you have to detect the image format and use
low-level interface such as TIFFReadEncodedStrip and
TIFFReadEncodedTile
My end goal is to be able to get 16 bit values for the R,G and B channels, as well as actually know where they are in the image.
In the docs for libtiff they provide this sample code for eading using the strip encoding, however, they don't explain what is in the buf you get or how to make use of it. Can anyone explain how I am supposed to read the 16 bit values from that? Also, is there a way to consistent determine the strip order so I my read image is rendered correct?
TIFF* tif = TIFFOpen("myfile.tif", "r");
if (tif) {
tdata_t buf;
tstrip_t strip;
buf = _TIFFmalloc(TIFFStripSize(tif));
for (strip = 0; strip < TIFFNumberOfStrips(tif); strip++)
TIFFReadEncodedStrip(tif, strip, buf, (tsize_t) -1);
_TIFFfree(buf);
TIFFClose(tif);
}
I am trying to get a set of binary images' eccentricity and solidity values using the regionprops function. I obtain the label matrix using the vision.ConnectedComponentLabeler function.
This is the code I have so far:
files = getFiles('images');
ecc = zeros(length(files)); %eccentricity values
sol = zeros(length(files)); %solidity values
ccl = vision.ConnectedComponentLabeler;
for i=1:length(files)
I = imread(files{i});
[L NUM] = step(ccl, I);
for j=1:NUM
L = changem(L==j, 1, j); %*
end
stats = regionprops(L, 'all');
ecc(i) = stats.Eccentricity;
sol(i) = stats.Solidity;
end
However, when I run this, I get an error says indicating the line marked with *:
Error using ConnectedComponentLabeler/step
Variable-size input signals are not supported when the OutputDataType property is set to 'Automatic'.'
I do not understand what MATLAB is talking about and I do not have any idea about how to get rid of it.
Edit
I have returned back to bwlabel function and have no problems now.
The error is a bit hard to understand, but I can explain what exactly it means. When you use the CVST Connected Components Labeller, it assumes that all of your images that you're going to use with the function are all the same size. That error happens because it looks like the images aren't... hence the notion about "Variable-size input signals".
The "Automatic" property means that the output data type of the images are automatic, meaning that you don't have to worry about whether the data type of the output is uint8, uint16, etc. If you want to remove this error, you need to manually set the output data type of the images produced by this labeller, or the OutputDataType property to be static. Hopefully, the images in the directory you're reading are all the same data type, so override this field to be a data type that this function accepts. The available types are uint8, uint16 and uint32. Therefore, assuming your images were uint8 for example, do this before you run your loop:
ccl = vision.ConnectedComponentLabeler;
ccl.OutputDataType = 'uint8';
Now run your code, and it should work. Bear in mind that the input needs to be logical for this to have any meaningful output.
Minor comment
Why are you using the CVST Connected Component Labeller when the Image Processing Toolbox bwlabel function works exactly the same way? As you are using regionprops, you have access to the Image Processing Toolbox, so this should be available to you. It's much simpler to use and requires no setup: http://www.mathworks.com/help/images/ref/bwlabel.html
I am trying to save an image using opencv cvSaveImage function. The problem is that I am performing a DCT on the image and then changing the coefficients that are obtained after performing the DCT, after that I am performing an inverse DCT to get back the pixel values. But this time I get the pixel values in Decimals(e.g. 254.34576). So when I save this using cvSaveImage function it discards all the values after decimals(e.g. saving 254.34576 as 254) and saves the image. Due to this my result gets affected. Please Help
"The function cvSaveImage saves the image to the specified file. The image format is chosen depending on the filename extension, see cvLoadImage. Only 8-bit single-channel or 3-channel (with 'BGR' channel order) images can be saved using this function. If the format, depth or channel order is different, use cvCvtScale and cvCvtColor to convert it before saving, or use universal cvSave to save the image to XML or YAML format."
I'd suggest investigating the cvSave function.
HOWEVER, a much easier way is to just write your own save/load functions, this would be very easy:
f = fopen("image.dat","wb");
fprintf(f,"%d%d",width,height);
for (y=0 to height)
for (x=0 to width)
fprintf(f,"%f",pixelAt(x,y));
And a corresponding mirror function for reading.
P.S. Early morning and I can't remember for the life of me if fprintf works with binary files. But you get the idea. You could use fwrite() instead.