Increase the resolution (dpi) of an image - c#

I'm not sure how feasible this will be without some thirdparty libraries, but here goes:
I have an image, 450x900 in size, which im trying to print.
The problem is, the method I'm using to print is sending raw data to the printer.
The resolution of the image is 96dpix96dpi, the printer runs at 203dpi.
So... the image comes out small.
I need to increase the dpi of the image to print it at its 'real' size.
Bitmap b0 = LoadBitmap();
//I need to rotate it because for some odd reason it prints backwards and upside down.
b0.RotateFlip(RotateFlipType.Rotate180FlipX);
//Set a new resolution, 203dpi
b0.SetResolution(203, 203);
//I need to save and reload the bitmap, because RotateFlip compresses it.
//(annoying as hell, took me ages to figure out why it wasn't working.)
Stream imgStream = new MemoryStream();
b0.Save(imgStream, ImageFormat.Bmp);
b0 = new Bitmap(imgStream);
//get my byte array
ImageConverter converter = new ImageConverter();
byte[] imageData = (byte[])converter.ConvertTo(b0, typeof(byte[]));
So, fairly straight forward.
But SetResolution(...) doesn't actually seem to do anything.
The image prints exactly the same size, and the resultant byte array is exactly the same size.
So I'm trying to work out what it is actually doing.
But I guess it would need to pad out all the image data with extra pixels to do what I want it to?
If this isn't a practical method, is there a simple stretch method or similar I can use to get the desired effect?

Why not scale your image to be bigger:
System.Drawing.Bitmap b0 = LoadBitmap();
double scale = 203/96;
int width = (int)(b0.Width * scale);
int height = (int)(b0.Height * scale);
System.Drawing.Bitmap bmpScaled = new System.Drawing.Bitmap(b0,width, height);

You have to understand that simply changing resolution (dpi) of a bitmap does nothing to its pixel content. For bitmap "resolution" is only a metadata. What really matter is size in pixels.
So if you want your picture to be bigger in print you'll have to either change printer resolution or scale your image (for example, using Grzegorz's method).

Related

A Graphics object cannot be created from an image that has an indexed pixel format [duplicate]

I am getting error:
"A Graphics object cannot be created from an image that has an indexed
pixel format."
in function:
public static void AdjustImage(ImageAttributes imageAttributes, Image image)
{
Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);
Graphics g = Graphics.FromImage(image);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(image, rect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, imageAttributes);
g.Dispose();
}
I would like to ask you, how can I fix it?
Refering to this, it can be solved by creating a blank bitmap with the same dimensions and the correct PixelFormat and the draw on that bitmap.
// The original bitmap with the wrong pixel format.
// You can check the pixel format with originalBmp.PixelFormat
Bitmap originalBmp = (Bitmap)Image.FromFile("YourFileName.gif");
// Create a blank bitmap with the same dimensions
Bitmap tempBitmap = new Bitmap(originalBmp.Width, originalBmp.Height);
// From this bitmap, the graphics can be obtained, because it has the right PixelFormat
using(Graphics g = Graphics.FromImage(tempBitmap))
{
// Draw the original bitmap onto the graphics of the new bitmap
g.DrawImage(originalBmp, 0, 0);
// Use g to do whatever you like
g.DrawLine(...);
}
// Use tempBitmap as you would have used originalBmp embedded in it
return tempBitmap;
The simplest way is to create a new image like this:
Bitmap EditableImg = new Bitmap(IndexedImg);
It creates a new image exactly like the original was with all its contents.
Overall, if you want to work with indexed images and actually preserve their colour depth and palette, this will always mean writing explicit checks and special code for them. Graphics simply can't work with them, because it manipulates colours, and the actual pixels of indexed images contain no colours, just indices.
For anyone still seeing this all these years later... the valid way to paint an image onto an existing (8-bit) indexed image is this:
Go over all the pixels of the image you want to paste and, for each colour, find the closest match on the target image's colour palette, and save its index into a byte array.
Open the backing bytes array of the indexed image using LockBits, and paste your matched bytes onto it, at the desired location, by looping over the relevant indices using the height and image stride.
It's not an easy task, but it's certainly possible. If the pasted image is also indexed, and contains more than 256 pixels, you can speed up the process by doing the colour matching on the palette instead of on the actual image data, then getting the backing bytes from the other indexed image, and remapping them using the created mapping.
Note that all of this only applies to eight bit. If your image is four-bit or one-bit, the simplest way to handle it is to convert it to 8-bit first so you can handle it as one byte per pixel, and convert it back afterwards.
For more information on that, see How can I work with 1-bit and 4-bit images?
Though the accepted answer works, it creates a new 32bpp ARGB image from the indexed bitmap.
To manipulate indexed bitmaps directly you can use this library (alert: shameless self promotion). Its GetReadWriteBitmapData extension allows creating a writable managed accessor even for indexed pixel formats.
And then you can use one of the DrawInto methods that can be used similarly to Graphics.DrawImage. Of course, as the target bitmap is indexed, the drawing operation must quantize the pixels using the target palette colors but there are a sort of overloads that can use dithering to preserve more image details.
Usage example (see more examples in the links above):
using (IReadWriteBitmapData indexedTarget = myIndexedBitmap.GetReadWriteBitmapData())
using (IReadableBitmapData source = someTrueColorBitmap.GetReadableBitmapData())
{
// or DrawIntoAsync if you want to use async-await
source.DrawInto(indexedTarget, targetRect, OrderedDitherer.Bayer8x8);
}
Image examples:
All images below had been created with PixelFormat.Format8bppIndexed format with the default palette, and a 256x256 icon and an alpha gradient rainbow were drawn on top of each other. Note that blending is used as much as possible with the available palette.
Image
Description
No dithering
Ordered Bayer8x8 dithering
Floyd-Steinberg error diffusion dithering
Disclaimer: Of course, the library has also some limitations compared to Graphics, for example there are no shape-drawing methods. But in worst case you still can use the accepted answer, and then call the ConvertPixelFormat method in the end if you need to produce an indexed result.

Compress Image in .NET CORE MVC [duplicate]

I need to shrink every image I get that is more than 10MB.
File types are png, jpg and gif.
I saw that ImageSharp has an option to resize an image:
Resize(new ResizeOptions
{
Mode = ResizeMode.Max,
Size = new Size(maxFileSize)
}
I saw a lot of examples using the resize function with width and height, but none using this one size option, and no documentation explain exactly what "size" means.
I've tried the following: Shrinking an image of 22.2MB using maxFileSize=1024 yielded a picture of 527.9MB.
"Shrinking" the same image with maxFileSize=1024*2*10 yielded a 47.4MB picture.
How can I shrink an image to a size of roughly 10MB (can be a bit less)?
My goal here is to limit to 10MB, and, if exceeded, reduce the image to the maximal possible size under 10MB without affecting the ratio.
I don't think that there is an easy (and reliable) way to compute the compressed size of an image, without, well, compressing it.
While ImageSharp doesn't seem to offer a native API for this, we can use existing functionality to find an optimal size which satisfies our constraints.
Idea:
We can assume that smaller images take less space on disk, i.e., the file size is correlated with the number of pixels. This may not hold for some sophisticated compression algorithms on very similar image sizes; however, even then we should still able to find a good estimation for the perfect image size.
Pick some rough lower and upper bounds for the image size. In the best case, our perfect image size lays somewhere in between.
Perform a binary search between the aforementioned bounds, by repeatedly resizing and compressing our image, until it has our desired file size.
Corresponding C# code:
// Load original image
using Image image = Image.Load("image.jpg");
// Configure encoder
var imageEncoder = new JpegEncoder
{
Quality = 95,
Subsample = JpegSubsample.Ratio420
};
// Resize image, until it fits the desired file size and bounds
int min = ESTIMATED_MINIMUM_WIDTH;
int max = ESTIMATED_MAXIMUM_WIDTH;
int bestWidth = min;
using var tmpStream = new MemoryStream();
while(min <= max)
{
// Resize image
int width = (min + max) / 2;
using var resizedImage = image.Clone(op =>
{
op.Resize(new ResizeOptions
{
Mode = ResizeMode.Max,
Size = new Size(width, MAXIMUM_HEIGHT)
});
});
// Compress image
tmpStream.SetLength(0);
resizedImage.Save(tmpStream, imageEncoder);
// Check file size of resized image
if(tmpStream.Position < MAXIMUM_FILE_SIZE)
{
// The current width satisfies the size constraint
bestWidth = width;
// Try to make image bigger again
min = width + 1;
}
else
{
// Image is still too large, continue shrinking
max = width - 1;
}
}
// Resize and store final image
image.Mutate(op =>
{
op.Resize(new ResizeOptions
{
Mode = ResizeMode.Max,
Size = new Size(bestWidth, MAXIMUM_HEIGHT)
});
});
// Store resized image
image.Save("image-resized.jpg", imageEncoder);
This loads the image "image.jpg" and finds the width bestWidth which yields a file size smaller than, but near to MAXIMUM_FILE_SIZE.
The other constants are defined as follows:
ESTIMATED_MINIMUM_WIDTH: The estimated lower bound for the perfect image width. The image will never become smaller than this. Should be at least 0.
ESTIMATED_MAXIMUM_WIDTH: The estimated upper bound for the perfect image width. The image will never become larger than this. Should be at most image.Width.
MAXIMUM_HEIGHT: The maximum image height. This is helpful if there are other constraints than the file size (e.g., the image must fit a certain screen size); else, this can be simply set to image.Height.
While binary search offers good algorithmic complexity, this can still be quite slow for very large images. If time is important (the question doesn't specify this), the performance can be improved by using good initial estimations of the upper and lower bounds for bestWidth, e.g., by linearly interpolating the file size through the ratio of pixels to bytes.
After looking some more into the documentation I now see the single int constructor simply insert the same number to both width and height.
For now, I shrink the image way too much using the following:
using (var image = Image.Load(imageFile))
{
var proportion = (double)imageFile.Length / maxFileSize;
var maxWidth = (int)(image.Width / proportion);
var maxHeight = (int)(image.Height / proportion);
image.Mutate(x => x
.Resize(new ResizeOptions
{
Mode = ResizeMode.Max,
Size = new Size(maxWidth, maxHeight)
}));
// Save the Image
}
If anyone has better Idea I would love to hear it.
The goal is to have the image shrink to just under 10MB (as close as possible).

C# Resizing Bitmap without changing the Pixelformat

I have a bitmap in BGR format and resolution 1920*1200 and want to scale it without changing the pixelformat.
private Bitmap rescale(Size size, Bitmap origin)
{
Bitmap scaled = new Bitmap(origin, size);
return scaled;
}
the problem is, that scaled turns out to be a RGBA bitmap(all alphas at 255), which is not only useless to me, but also troubles me as later I am doing a AbsDiff (from EMGU) on different images and then, the alpha value always turns out zero..
Is there a way to not change the pixelformat when scaling or to do AbsDiff without the alpha-values? Because like this, when I load the image later, it is invisible..
Bitmap scaled = new Bitmap(origin, size);
There are a lot of implicit assumptions built into that constructor call. You'll get:
A bitmap with the 32bppPArgb pixel format. Meant to help the programmer fall into the pit of success, it is the most optimal pixel format on modern PCs. Compatible with the pixel format of the video adapter frame buffer, it can be blitted without any conversion. It is ten times faster than all the other ones.
The resolution is set to the video adapter DPI. This is usually a bit less optimal although it is pretty hard to argue that it should use the resolution of the source image after rescaling it. You might want to modify that.
A transparent background. That matters if the source bitmap has transparency or has pixels with the alpha channel set to a value < 255. Usually fine, if the source bitmap was transparent then the new one will be as well. Not so fine with alpha, rescaling the bitmap is pretty likely to affect that negatively. YMMV.
Bilinear interpolation of the source image. That is fairly modest, you might favor InterpolationMode.HighQualityBicubic for a better result, especially when you shrink it by more than 50%. Or NearestNeighbor if speed is your concern or the source image is very small and you enlarge it with the intention to keep the pixels visible as-is.
Clearly you are unhappy, the first bullet is the source of your complaint. Writing it out with all details tweakable:
public static Bitmap RescaleImage(Image source, Size size) {
// 1st bullet, pixel format
var bmp = new Bitmap(size.Width, size.Height, source.PixelFormat);
// 2nd bullet, resolution
bmp.SetResolution(source.HorizontalResolution, source.VerticalResolution);
using (var gr = Graphics.FromImage(bmp)) {
// 3rd bullet, background
gr.Clear(Color.Transparent);
// 4th bullet, interpolation
gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
gr.DrawImage(source, new Rectangle(0, 0, size.Width, size.Height));
}
return bmp;
}
-
The (admittedly sort of ugly) way I would do this is to create a new Bitmap with the original Pixelformat and the new Size and draw the original bitmap onto it (untested):
private Bitmap rescale(Size size, Bitmap origin)
{
Bitmap rescaled = new Bitmap(size.Width, size.Height, origin.PixelFormat);
using(Graphics g = Graphics.FromImage(rescaled))
{
g.DrawImage(origin, 0, 0, size.Width, size.Height);
}
return rescaled;
}
This seems to be what you need:
http://www.codeproject.com/Tips/552141/Csharp-Image-resize-convert-and-save
and
Bitmap(Image, Size) constructor
public Bitmap(
Image original,
Size newSize
)
This is a constructor that takes a new size. The image is automatically resized to that size.

Graphics on indexed image

I am getting error:
"A Graphics object cannot be created from an image that has an indexed
pixel format."
in function:
public static void AdjustImage(ImageAttributes imageAttributes, Image image)
{
Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);
Graphics g = Graphics.FromImage(image);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(image, rect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, imageAttributes);
g.Dispose();
}
I would like to ask you, how can I fix it?
Refering to this, it can be solved by creating a blank bitmap with the same dimensions and the correct PixelFormat and the draw on that bitmap.
// The original bitmap with the wrong pixel format.
// You can check the pixel format with originalBmp.PixelFormat
Bitmap originalBmp = (Bitmap)Image.FromFile("YourFileName.gif");
// Create a blank bitmap with the same dimensions
Bitmap tempBitmap = new Bitmap(originalBmp.Width, originalBmp.Height);
// From this bitmap, the graphics can be obtained, because it has the right PixelFormat
using(Graphics g = Graphics.FromImage(tempBitmap))
{
// Draw the original bitmap onto the graphics of the new bitmap
g.DrawImage(originalBmp, 0, 0);
// Use g to do whatever you like
g.DrawLine(...);
}
// Use tempBitmap as you would have used originalBmp embedded in it
return tempBitmap;
The simplest way is to create a new image like this:
Bitmap EditableImg = new Bitmap(IndexedImg);
It creates a new image exactly like the original was with all its contents.
Overall, if you want to work with indexed images and actually preserve their colour depth and palette, this will always mean writing explicit checks and special code for them. Graphics simply can't work with them, because it manipulates colours, and the actual pixels of indexed images contain no colours, just indices.
For anyone still seeing this all these years later... the valid way to paint an image onto an existing (8-bit) indexed image is this:
Go over all the pixels of the image you want to paste and, for each colour, find the closest match on the target image's colour palette, and save its index into a byte array.
Open the backing bytes array of the indexed image using LockBits, and paste your matched bytes onto it, at the desired location, by looping over the relevant indices using the height and image stride.
It's not an easy task, but it's certainly possible. If the pasted image is also indexed, and contains more than 256 pixels, you can speed up the process by doing the colour matching on the palette instead of on the actual image data, then getting the backing bytes from the other indexed image, and remapping them using the created mapping.
Note that all of this only applies to eight bit. If your image is four-bit or one-bit, the simplest way to handle it is to convert it to 8-bit first so you can handle it as one byte per pixel, and convert it back afterwards.
For more information on that, see How can I work with 1-bit and 4-bit images?
Though the accepted answer works, it creates a new 32bpp ARGB image from the indexed bitmap.
To manipulate indexed bitmaps directly you can use this library (alert: shameless self promotion). Its GetReadWriteBitmapData extension allows creating a writable managed accessor even for indexed pixel formats.
And then you can use one of the DrawInto methods that can be used similarly to Graphics.DrawImage. Of course, as the target bitmap is indexed, the drawing operation must quantize the pixels using the target palette colors but there are a sort of overloads that can use dithering to preserve more image details.
Usage example (see more examples in the links above):
using (IReadWriteBitmapData indexedTarget = myIndexedBitmap.GetReadWriteBitmapData())
using (IReadableBitmapData source = someTrueColorBitmap.GetReadableBitmapData())
{
// or DrawIntoAsync if you want to use async-await
source.DrawInto(indexedTarget, targetRect, OrderedDitherer.Bayer8x8);
}
Image examples:
All images below had been created with PixelFormat.Format8bppIndexed format with the default palette, and a 256x256 icon and an alpha gradient rainbow were drawn on top of each other. Note that blending is used as much as possible with the available palette.
Image
Description
No dithering
Ordered Bayer8x8 dithering
Floyd-Steinberg error diffusion dithering
Disclaimer: Of course, the library has also some limitations compared to Graphics, for example there are no shape-drawing methods. But in worst case you still can use the accepted answer, and then call the ConvertPixelFormat method in the end if you need to produce an indexed result.

Copy one Bitmap onto a larger Bitmap using without using Graphics.DrawImage

This is a follow up from Rendering to a single Bitmap object from multiple threads
What im trying to achieve is to take a bitmap of say 50x50 pixels and draw it onto a larger bitmap(100x100 pixels) at any point on the larger image, using the bitmaps LockBits function or any other but NOT graphics.DrawImage. My reasons for not wanting to use DrawImage is stated in the other thread.
I have managed to get something by using Marshal.Copy from the source BitmapData to the dest BitmapData but its creating a tiled, stretched image horizontally.
You could manipulate the image in memory without relying on any system calls. If you dig into the underlying format of a .BMP file you could build your own Device Independant Bitmap class that truly "understands" the low level format of a .BMP.
For example a 8 bit per pixel image is essentially a 2 dimensional array of bytes (each byte is 1 pixel) plus a simple color table. Roughly speaking (and this is very very rough):
byte[,] bColors = new byte[3,256]; // 256 RGB colors
byte[,] bImage = new byte[25,50]; // 25 x 50 pixels
The trick is (as always) getting a hold of the raw pixel data, doing the processing, and then updating the raw pixel data with your changes.
In the past I've approached this by converting a GDI HBITMAP into a 24bpp DIB, doing my funky image processing on the raw pixels (3 bytes per pixels makes this easier), then converting the DIB back into a HBITMAP. This was all using just classic GDI (pre GDI+ even, let alone C#).
Using that approach you could design a control structure to allow multiple writers to different sections of your much bigger image.
However... the lowlevel BitBlt GDI calls are likely to be way more efficient that anything you can do. If I were you I'd make certain that just doing 50 or 100 bitblt's in a row would be too slow (you'd likely need to do this in c++).
The most annoying challenges with dealing with DIB's are:
Converting a DIB to an actual "image" ready for display and
Converting an actual "image" into a DIB
Saving a DIB as something other than a .BMP
Core references when I started learning the "horror" that images actually are:
http://msdn.microsoft.com/en-us/library/dd183562(VS.85).aspx
http://msdn.microsoft.com/en-us/library/dd144879(VS.85).aspx
http://msdn.microsoft.com/en-us/library/dd162973(VS.85).aspx
How you go about getting to/from .NET Image's... well... that's a good question :)
This should work just fine using LockBits/BitmapData, if you are using a 32bpp [P]ARGB pixel format. The trick is that you will have to copy the data one row at a time so that it aligns in the correct places. You should be able to do this using something like:
Rectangle srcArea = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height);
BitmapData srcData = srcBitmap.LockBits(srcArea, ImageLockMode.ReadOnly, destBitmap.PixelFormat);
Rectangle destArea = new Rectangle(25, 25, srcBitmap.Width, srcBitmap.Height);
BitmapData destData = destBitmap.LockBits(destArea, ImageLockMode.WriteOnly, destBitmap.PixelFormat);
IntPtr srcPtr = srcData.Scan0;
IntPtr destPtr = destData.Scan0;
byte[] buffer = new byte[srcData.Stride];
for (int i = 0; i < srcData.Height; ++i)
{
Marshal.Copy(srcPtr, buffer, 0, buffer.Length);
Marshal.Copy(buffer, 0, destPtr, buffer.Length);
srcPtr += srcData.Stride;
destPtr += destData.Stride;
}
srcBitmap.UnlockBits(srcData);
destBitmap.UnlockBits(destData);
As a warning, this code won't work as is because I am not sure what the right incantations are for incrementing IntPtr's. I've done this same type of thing before, but in C++. Also, I don't know if there is a way to directly copy the data instead of using an intermediate buffer.
An additional caveat: the LockBits call srcBitmap and the sizing of the buffer assume that srcBitmap will be completely enclosed in destBitmap. If this is not the case (some part of the bitmap will be cropped off) the area locked and the size of the buffer will need to be adjusted.
If you are not using a 32bpp pixel format (ie 24bpp), it will be more difficult. The stride of your source BitmapData may include some amount of padding that should not be copied. You could work around this by calculating the amount of actual pixel data in a source row, and copy this amount. Indexed pixel formats would be even more work.
I would recommend taking a look at the internal bitmap memory structure.
The best approach, I think, would be to not try to set the BitmapData directly. Instead, I would make a single, shared byte array of the appropriate size, and set the byte array directly from your smaller images.
When you compose your larger image, you can take the final byte array and directly make a Bitmap from the byte data.
This has the advantage of allowing you to control the memory management, thread the operations, etc, as you seemed to want to do in your original post. It should be very fast for the data access, as well.

Categories

Resources