I'm writing an image viewer application that loads large still images. I must be able to zoom in to 1:1 to measure exact pixel coordinates of features in the image. I'm using the Viewport control posted here that works great for zoom and panning. I can load a 150 MP tiff image with BitmapDecoder and its pixel count is correct 14000 x 10000. However, when I assign this bitmap to the Image.Source property it gets decimated to roughly 15MP:
sourceBitmap.PixelWidth = 14204
sourceBitmap.PixelHeight = 10652
After assigning this bitmap to image1.Source we get
image1.Source.Width = 4545.27...
image1.Source.Height = 3408.64...
I'm aware of the unitless context of WPF graphics, and can work back the scale factors to read original coordinates, but there is a risk of rounding errors, and I'm working on a scaled copy that degrades the original image resolution.
According to the Microsoft documentation, a WPF bitmap can be up to 64GB in size, but the Image control seems not to be designed to work with bitmaps larger than 15MP. Setting Stretch to "None" makes things worst. It trims the image to the top left 4545 x 3408 pixels of the source and it displays it very small, almost as a thumbnail instead of 1:1.
Is there any way around this limitation?
In contrast to a BitmapSource's PixelWidth and PixelHeight, the Widthand Height values depend on its DPI (dots per inch), which is a TIFF or EXIF tag in the image file.
The values are identical when the resolution is 96 DPI, otherwise calculated as
Width = PixelWidth * 96 / DpiX
Height = PixelHeight * 96 / DpiY
Apparently, your images are tagged with 300 DPI.
This does in no way affect the pixel count, but just determines the native, unstretched size of the bitmap when it is shown in an Image element or ImageBrush.
Instead of using Width and Height, just keep using PixelWidth and PixelHeight:
var bitmap = (BitmapSource)image1.Source;
var width = bitmap.PixelWidth;
var height = bitmap.PixelHeight;
Related
Quick question:
I have this 1000 x 1000 bitmap image:
and I use this routine to load it:
private BitmapSource initialBitmap = new BitmapImage(new Uri("C:\\Users\\...\\Desktop\\Original.bmp"));
Why after I load it, and right after I step over the above line, I see it as 800 x 800?
P.S I want it to be 1000 x 1000 and without using any Resize functions. It was working and suddenly it is 800*800 !
The values returned by BitmapSource.Width and BitmapSource.Height are not in pixels, but rather WPF's device-independent units, which are always 96 dpi. E.g.:
Gets the width of the bitmap in device-independent units (1/96th inch per unit).
If you want to know the actual pixel width and height, you need to use the PixelWidth and PixelHeight properties.
Your question isn't very specific, but if what you are actually concerned about is having the bitmap display at the same size in which it was authored, then the easiest solution is to make sure you author it at 96 dpi. Whatever program you're using to author the bitmap likely has a place where you can set the bitmap resolution. Typically this can be set with or without changing the pixel dimensions of the image (i.e. scaling the image larger or smaller); you want to do it without scaling the image, so that the pixel dimensions remain the same but the dpi changes to match what WPF is using.
Note that this still won't guarantee the bitmap displays at a specific pixel size. The display resolution can be and often is different from 96 dpi, in which case WPF will scale images to ensure that the physical dimensions of the image (i.e. the dimensions in inches, millimeters, etc.) are correct according to the information in the bitmap. For example, 960 pixels wide at 96 dpi means 10" wide. On a 120 dpi display, this means displaying the bitmap large enough so that its width uses 1200 display pixels.
If you want or need the bitmap to display at exactly the same number of display pixels regardless of the display resolution, then you'll have to set a transform where you display the image to reverse the effect of the scaling that WPF would otherwise do. This requires knowing the display resolution, of course.
Here are some other related Stack Overflow questions which you might find useful:
RenderTargetBitmap renders image of a wrong size
WPF for LCD screen Full HD
Screen Resolution Problem In WPF?
This is by design. Note the MSDN documentation for BitmapSource.Width/Height:
Gets the width of the bitmap in device-independent units (1/96th inch
per unit). (Overrides ImageSource.Width.)
Instead you should use the PixelWidth / PixelHeight property:
Gets the width of the bitmap in pixels.
A rather confusing choice or terms, imo, but there you go..
I am really struggling to get this right, any help would be appreciated.
I have a series of images that I want to build in to a PDF using MigraDoc (1 image = 1 page)
Each image must be displayed on a separate page but may not extend over the page it must fit on to the page perfectly.
So, how do I scale an image (of any size) to fit to a page using MigraDoc?
You call AddImage() to add the image - and in return you get an Image object that allows you to set width and/or height of the image.
What you have to do: check the dimensions of the image, calculate which is the limiting factor (width or height), then set this limiting factor on the Image object and also set LockAspectRatio.
Or set both Width and Height and leave LockAspectRatio off.
For DIN A4, you may allow e.g. 19 cm x 27.7 cm as maximum image size.
For an image with 1000x1000 pixel you would set the width to 19 cm (assuming LockAspectRatio is on). Height will then also be 19 cm automatically.
For an image with 1000x2000 pixel you would set the height to 27.7 cm. Width will then be 50% of the height.
I have a Bitmap variable and I copy smaller 32x32 png files (loaded as Bitmaps) onto the bitmap. However, some png's are scaled up (always the same ones) and appear as 36x36 for example after copying. Almost as if some png's have another DPI or something? How can I prevent this?
Graphics g = Graphics.FromImage(destinationImage);
g.DrawImage(sourceImage, location); // sourceImage is sometimes larger than it actually is. On disk it is 32x32 but after copying it might be bigger...
g.Dispose();
I guess you are right about DPI, as it's stated in the documentation:
This method draws an image using its physical size...
I'm too lazy to make a test project, but I think Graphics.DrawImage(Image, Rectangle) with rectangle size equals to source image size will fix your problem.
The Image.Horizontal/VerticalResolution property matters. If it doesn't match the dots-per-inch setting of your monitor then the image is going to be drawn proportionally larger or smaller. This tends to be undesirable, use the DragImage(Image, Rectangle) overload to force it to display at exact 32 x 32 pixels.
I'm writing an application to send some images to a third party, and the images must be 200x200 DPI. The image is a Bitmap and is sized at 500 width and 250 height.
The first time I tested the images with the third party, my resolution was incorrect. I merely used image.SetResolution(200,200) to correctly set it to 200x200. This, however, only changed the resolution tag for the image and did not properly, according to my third party technical contact, adjust the image height and width.
Is there a ratio that I can use so that for each X units I increment the resolution, I merely increment the corresponding height or width Y units? I thought that I could just increment resolution without having to increment height or width.
Thank you,
Aaron.
An image stored digitally has no meaningful concept of DPI. DPI comes into play when reproducing an image on a physical device.
You need to adjust the image size with regard to the DPI of the physical device, and the desired size of the output on that device.
For example, if a printer tells you they need an image at 300dpi to fill a space of 4in x 4in then you would provide them a bitmap with a size of 1200x1200 pixels. This image would end up with a physical size of 4in x 4in on a 300dpi output device. On a 600dpi device the same image would have an output size of 2in x 2in.
When dealing with digital images, you usually refer to PPI, which is pixels per inch. DPI is not directly related to digital image resolution.
So, if you look at a image that is 200px by 200px # 200PPI, you will have an image that is 1 inch by 1 inch.
I'm doing some standard code, I am applying a logo to an image and it is working fine.
The source image is always 1024 x 768 as the code before this takes the image and resizes it (creates a new file based on the original).
The logo is applied correctly on some images I have that are 2288 x 1712. If I use an image of 3264 x 2448 then the logo is added at the correct start co ordinates but carries on the x and y axis.
The logo should have a 10px gap between the sides. The 2 letters in the logo that you can see are also far larger than the source image logo.
If I take the image that is doing the wrong behaviour (3264 x 2448) and change it to 2288 x 1712 and then run the code, it outputs the correct result!
I do not understand because the variable sourceImg is always the 1024 x 768 version so why should resizing the original image have an impact?
Image sourceImg = Image.FromFile(Path.Combine(filepath,filename));
Image logo = Image.FromFile(watermark);
Graphics g = Graphics.FromImage(sourceImg);
g.DrawImage(
logo,
sourceImg.Width - horizontalPosition - logo.Width,
sourceImg.Height - verticalPosition - logo.Height
);
g.Dispose();
logo.Dispose();
sourceImg.Save(Path.Combine(filepath, filename));
sourceImg.Dispose();
The overload of the DrawImage that you are using takes the PPI values of the images to calculate the size. As the PPI value of the logo is lower than the PPI value of the image, the logo is scaled up so that their relative physical size (inches instead of pixels) are the same.
Use an overload where you specify the size in pixels:
g.DrawImage(
logo,
sourceImg.Width - horizontalPosition - logo.Width,
sourceImg.Height - verticalPosition - logo.Height,
logo.Width,
logo.Height
);