I am trying to let a user capture an image and add it onto the screen using an Image. However, I also need to resize this image down to about half size due to memory restrictions (12x 5MP images is never good on a phone...)
I am launching the camera task fine and it calls the Completed event. However, when I try and use DecodeJpeg I get a "The parameter is incorrect." exception.
Here is my code for resizing, where mx and my are int for dimensions. I have verified that there is something in the e.ChosenPhoto with a length of about ~5500:
WriteableBitmap bitmap = PictureDecoder.DecodeJpeg(e.ChosenPhoto, mx, my);
Image img = new Image();
img.Source = bitmap;
The first line is where the app crashes. Any ideas?
EDIT:
This also occurs with the result from the PhotoChooserTask....
Try using the System.Windows.Media.Imaging - Extensions.LoadJpeg method instead of PictureDecoder.DecodeJpeg. Also make sure that the stream is positioned at the beginning of the stream. If you have already used the stream you will need to reset it using:
MyImageStream.Seek(0, System.IO.SeekOrigin.Begin)
I had a lot of problems trying to get access to the original image, especially since BitmapImage automatically resizes images over 2000x2000. If you want an image larger than 2000x2000 you have to have access to the original stream and load it into a WriteableBitmap object
If you want to see some more complex image handling code including detecting resolution from image stream using ExifLib and rotating stream using the WriteableBitmap Extensions check out the BarcodeCaptureResult class for the Silverlight ZXing Library.
UPDATE: Since all you want is to resize an image given the e.ChosenPhoto result I pulled the code from The Silverlight ZXing library. This should work:
WriteableBitmap wbBarcodeImage = new WriteableBitmap(mx, my);
Extensions.LoadJpeg(wbBarcodeImage, e.ChosenPhoto);//Load JPEG from stream into our re-sized writeable bitmap
Note that you will need to use the correct height/width ratio, otherwise you will have a black bar at the bottom or side of the image. You can use ExifLib to detect the original image size and use that to scale (see GetWriteableBitmap method in BarcodeCaptureResult linked above)
Related
I have a bitmap (from a .bmp file) that is actually a small part of a larger screen dump that I took earlier.
Now I want to display that bitmap in a PictureBox control (WinForms), but I have trouble getting it to display at the same ratio as when it was captured originally.
I assume it has something to do with Windows' DPI setting for my monitor, but how can I shortcut that and make the bitmap be displayed exactly as when captured?
I figured it out! The problem was not WinForms, but the horizontal and vertical resolution associated with the displayed bitmap. When copying the part of the original screendump into a new bitmap (the one that is later assigned to the Image property of the PictureBox), the new bitmap had a different (=default) resolution setting. By changing the resolution setting to match the one from the source bitmap (the original screendump), the problem went away!
I have an BitmapImage which is generated by application itself and have no sourceFile or URI. Now i have to convert this image to WritableBitmapImage in order to save it, according to this. But all the methods require sourceFile or URI.
There is no good way to extract the pixels from a BitmapImage after the fact.
As you note, if you have the source image then you can create a WriteableBitmap from that.
Since WritableBitmap and BitmapImage are both ImageSources they can be used the same way in most cases, so if you know you'll need access to the pixels when you create the BitmapImage then you can usually create a WriteableBitmap instead.
Once the BitmapImage is created and the original source is no longer available the closest you can get is to use RenderTargetBitmap to render the displayed Image control into a new bitmap from which you can extract the pixels with GetPixelData.
This will be a second generation image though and for large images will likely have lost data to resizing interpolation between the original and the rendering.
I'd recommend using a WriteableBitmap instead of a BitmapImage to begin with when generating the original image.
Right now I have an application that loads a bunch of thumbnail images into PictureBox controls. Sometimes over a hundred at a time. The images are created using Image.FromStream(MemoryStream), and the memory stream represents each JPG file.
Currently, as soon as I am calling Image.FromStream, I assign the reference to the picture box, then immediately after I Flush() and Dispose() the stream.
According to documentation, I should not release the stream until I'm done with the image. However, I have not had any errors doing this. My PictureBox controls can repaint themselves all day long with the thumbnail images even after the memory streams have long been disposed.
However, if I try to access these images in any way, such as calling the Save function, it throws an error.
If a Bitmap has a dependency on a memory stream or file stream, will it dispose of the source when you dispose of it? Or is this only when it creates the stream itself using Bitmap.FromFile?
Is disposing of the MemoryStream immediately like I am doing considered OK? The PictureBox must be caching the image in some way. I've never had an error because I don't need to touch the image once it has been set.
I also notice that it takes my PictureBoxes a long time to paint on their parent control. I am not sure if this is because I have too many controls, or because there is a delay the first time an image is painted in a PictureBox.
I know that when you create a new Bitmap with a file parameter, it doesn't actually load the file contents until it is first needed. My issue is, I don't want each OnPaint operation to be delayed by a file read, I want to make sure the data is preloaded before I try to paint the controls, otherwise I get flickering and slow painting.
Is there a good way to force an image to be preloaded? What are the discrepancies between Image.FromFile, Image.FromStream, new Bitmap(fileName), etc? Do any of these load all the bytes right away, or is it delayed until they are first needed?
Here's my code:
MemoryStream ms = new MemoryStream(bytes); // byte[] array
pictureBox.Image = Image.FromStream(ms);
ms.Flush();
ms.Dispose();
GDI+ which underlies the Image classes require that the source stream remain open while the Image exists to work properly. It's also a bad idea to write changes to the source stream while Image is using it. Changes should be saved to a different stream.
Everything derived from Image uses a Stream under the covers. Their loading behaviors are the same.
Image does not Close or Dispose a stream that was provided to it via FromStream. You have to manage that yourself.
Also, calling Flush on a Stream only has an effect if the Stream has been written to.
If you're looking to improve UI behavior for your PictureBoxes, try this:
Set InitialImage to a small spinner GIF.
Set WaitOnLoad to false.
Specify the image by setting ImageLocation. (You can use a filespec or a URL in it.)
Load the images asynchronously using the LoadAsync method.
1)
If a Bitmap has a dependency on a memory stream or file stream, will
it dispose of the source when you dispose of it? Or is this only when
it creates the stream itself using Bitmap.FromFile?
Take a look at Image.FromStream Method (Stream, Boolean):
You must keep the stream open for the lifetime of the Image.
2)
The PictureBox must be caching the image in some way. I've never had
an error because I don't need to touch the image once it has been set.
You just should not dispose the Image instance since it has been assigned to the PictureBox (i.e. picture box is using it) and while PictureBox uses it. Otherwise, PictureBox will try using disposed image and it should lead to some "not good" consequences.
PictureBox.Image Property:
Note If you want to use the same image in multiple PictureBox
controls, create a clone of the image for each PictureBox. Accessing
the same image from multiple controls causes an exception to occur.
Update:
Sample with practically the same scenario: HOW TO: Copy a Picture from a Database Directly to a PictureBox Control with Visual C#.
I've been fussing with this for the better part of the night, so maybe one of you can give me a hand.
I have found GDI+ DrawImage in C# to be far too slow for what I'm trying to render, and from the looks of forums, it's the same for other people as well.
I decided I would try using AlphaBlend or BitBlt from the Win32 API to get better performance. Anyway, I've gotten my images to display just fine except for one small detail—no matter what image format I use, I can't get the white background to disappear from my (transparent) graphics.
I've tried BMP and PNG formats so far, and verified that they get loaded as 32bppargb images in C#.
Here's the call I'm making:
// Draw the tile to the screen.
Win32GraphicsInterop.AlphaBlend(graphicsCanvas, destination.X, destination.Y, this.TileSize, this.TileSize,
this.imageGraphicsPointer, tile.UpperLeftCorner.X, tile.UpperLeftCorner.Y,
this.TileSize, this.TileSize,
new Win32GraphicsInterop.BLENDFUNCTION(Win32GraphicsInterop.AC_SRC_OVER, 0,
Convert.ToByte(opacity * 255),
Win32GraphicsInterop.AC_SRC_ALPHA));
For the record, AC_SRC_OVER is 0x00 and AC_SRC_ALPHA is 0x01 which is consistent with what MSDN says they ought to be.
Do any of you guys have a good solution to this problem or know a better (but still fast) way I can do this?
Graphics.DrawImage() speed is critically dependent on the pixel format. Format32bppPArgb is 10 times faster than any other one on any recent machine I've tried.
Also make sure you the image doesn't get resized, be sure to use a DrawImage() overload that sets the destination size equal to the bitmap size. Very important if the video adapter's DPI setting doesn't match the resolution of the bitmap.
Have you tried an opacity of just 255 rather than a calculated one?
This blog post describes what you're trying to do:-
http://blogs.msdn.com/andreww/archive/2007/10/10/preserving-the-alpha-channel-when-converting-images.aspx
Critical thing is that he carries out a conversion of the image to make the alpha channel compatible..
Ok. From a pure Win32 perspective:
In order for AlphaBlend to actually alpha blend... it needs the source device context to contain a selected HBITMAP representing an image with 32bpp bitmap with a pre-multiplied alpha channel.
To get a device bitmap with 32bpp you can either call one of the many functions that will create a screen compatible device bitmap and hope like hell the user has selected 32bpp as the desktop bitdepth. OR, ensure that the source bitmap is a DIBSection. Well, the library or framework that is creating it from the loaded image for you.
So, C# is loading your images with 32bpp argb, BUT, how are you converting that C# representation of the bitmap into a HBITMAP? You need to ensure that a DIB Section is being created, not a DDB (or device dependent bitmap), and that the DIB Section is 32bpp.
Trying to use 300dpi tif images for display on the web. At the moment, when the user uploads an image, I am dynamically creating a thumbnail. If a page is created referencing the high-res image with a width of 500x500px, can I use the same functionality to convert to a gif/jpg on the fly. What is the impending resolution of the jpg that would be created?
EDIT:
To further explain the usage, the user uploads 300dpi images which are approx 3000x3000 pixels. The user is using these images to create a catalog page which will be used for pdf printing. When they are creating the page, we only need 72dpi images to display to the screen, but for print, need the 300dpi images. Obviously they do not want to add a 3000x3000px image to the page, so it needs to be resized to the correct viewing area, e.g. 500x500px etc.
This boils down to a simple image resize. The discussion of DPIs is just ancillary data to calculate the scale factor.
As #Guffa said, you should do this at the time of the upload so that you can just serve static images in your viewer.
This will be a load on the server:
Load the full image. This will be about 27 MB of memory for your 3000x3000 images.
Resize. Lot's of math done lazily (still CPU intensive).
Compress. More CPU + Cost of writing to your drive.
Since you are already taking the time to generate a thumbnail, you can amortize that cost and this cost by not having to repeat Step 1 above (see the code).
After an image is uplaoded, I would recommend spinning off a thread to do this work. It's a load on the web server for sure, but you're only other option is to devote a second machine to performing the work. It will have to be done eventually.
Here is some code to do the job. The important lines are these:
OutputAsJpeg(Resize(big, 300.0, 72.0), new FileStream("ScreenView.jpg"));
OutputAsJpeg(Resize(big, bigSize, 64.0), new FileStream("Thumbnail.jpg"));
We can resize the big image however we need. In the first line we just scale it down by a fixed scale (72.0 / 300.0). On the second line, we force the image to have a final max dimension of 64 (scale factor = 64.0 / 3000.0).
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.IO;
BitmapSource Resize(BitmapSource original,
double originalScale,
double newScale) {
double s = newScale / originalScale;
return new TransformedBitmap(original, new ScaleTransform(s, s));
}
void OutputAsJpeg(BitmapSource src, Stream out) {
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(src));
encoder.Save(out);
}
// Load up your bitmap from the file system or whatever,
// then dump it out to a smaller version and a thumbnail.
// Assumes thumbnails have a max dimension of 64
BitmapSource big = new BitmapImage(new Uri("BigPage0.png",
UriKind. RelativeOrAbsolute));
double bigSize = Math.Max(big.PixelWidth, big.PixelHeight);
OutputAsJpeg(Resize(big, 300.0, 72.0), new FileStream("ScreenView.jpg"));
OutputAsJpeg(Resize(big, bigSize, 64.0), new FileStream("Thumbnail.jpg"));
If I understand what you want - you're trying to make a gif or jpg thumbnail of a very high resolution tif, for web display - if not, I apologize in advance....
If you want the thumbnail to be 500x500px, that is the resolution of the jpg/gif you'll want to create - 500x500, or at least 500x<500 or <500x500 (to fit in the box, unless you want distorted images).
For display on the web, the DPI does not matter. Just use the pixel resolution you wish directly.
Technically the JPG/GIF is created at 72-DPI by the Image classes in .NET. But the DPI really doesn't have any meaning to the browser - it just uses the dimensions 500x500.
When displaying an image on a web, the dpi (or more correctly ppi) setting is irrelevant. It's only the size in pixels that is relevant.
You can convert an image on the fly, but it is very work intensive for the server to do that every time the image is displayed. You should create the sizes that you need when the user uploads the image.