Precisely scale image - c#

I am trying to precisely and predictably scale an image in C# to a different resolution, both up and down. When I open the resulting images with external tools such as Gimp, the results are not satisfying with my current settings.
public Image Square(Image image, int res) {
Bitmap sq = new Bitmap(res, res, image.PixelFormat);
Graphics canvas = Graphics.FromImage(sq);
canvas.CompositingQuality = CompositingQuality.HighQuality;
canvas.SmoothingMode = SmoothingMode.None;
canvas.InterpolationMode = InterpolationMode.Bicubic;
canvas.DrawImage(sq, 0, 0, res, res);
return sq;
}
The results are okay when scaling down (but far from perfect), but there are side-effects when scaling up:
This picture has a resolution of 2x2 pixels. The alpha channel is set to opaque for all pixels.
After scaling it to 4x4 pixels, this is the result:
Apparently, the C# graphics library introduced transparency while scaling the picture. This method should still work if the given image has transparent pictures, so removing the alpha channel is not an option.
Similarly, when scaling pictures down, there are problems at the edges of the resulting images as well, usually either very dark or transparent.
Is there any way to circumvent this behavior?
Edit: I already tried NearestNeighbor for downscaling only, but it results in this:
Edit 2: With WrapMode.TileFlipXY, the transparent edge is gone, but red only makes up 25% of the image instead of 50% as it should be:

You're asking for bicubic interpolation and you're getting it. What you want is the "nearest neighbor" option as outlined in the docs:
canvas.InterpolationMode = InterpolationMode.NearestNeighbor;

A way to avoid edge artifacts is to wrap the image:
using (ImageAttributes wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
g.DrawImage(input, rect, 0, 0, input.Width, input.Height, GraphicsUnit.Pixel, wrapMode);
}
Direct copy/paste from:
Ghost-borders ('ringing') when resizing in GDI+

I believe you need to combine NearestNeighbor interpolation with Half pixel offset. As pointed out in a similar question here.
canvas.InterpolationMode = InterpolationMode.NearestNeighbor;
canvas.PixelOffsetMode = PixelOffsetMode.Half;

Related

C# WinForms | Picturebox white bleeding right & bottom edges

Hello Stackoverflow Community.
I currently work on an application which has a cursor region magnifyer feature, for the user to pick a color.
However, i've the problem that the Picturebox has white edges on the right and bottom, even though the image captured is only one dark color.
The captured screen is 10x10 pixel and the Picturebox is 80x80 pixel and it's SizeMode set to StretchImage.
I checked if the raw captured image maybe contains this edges already by saving it and checking it in Photoshop. But the raw captured image is fine, so it must be something weird with the Picturebox.
Here you can see how it looks (The mousecursor and the capture-region[green rectangle] are just painted on the screenshot for demonstration, as i was not able to screen my cursor, and yes - the green region is painted way too large, it should be just 10x10 pixel ^^)
Has it maybe something to do with the way the Image get's stretched internally? If so, is there any, not too complicated way around it?
I appreciate any help in advance :)
//Edit: I think it's actually about the stretching
I found this topic Image after resize has white border but i don't know what the GetSize() Method is, or where it is (from)
Have a great day!
I found a solution for the edges here, as you can see in the first screen. However, in the second screen you can see a 2x2 pixel capture, stretched to 80x80 pixel with the found "solution", will mix colors as it's actually 80x80 pixel in the end, while i want to have it displaying the raw pixels, so, 3 black ones and one white one in this case. Now i am even more stuck ^^
I guess i should instead read the pixel colors of the single pixels from captured image and set the colors in the preview picturebox, or make 4 panels in the case of just 2x2 pixels
public new Image Resize(Image image, int targetWidth, int targetHeight)
{
var resizedImage = new Bitmap(targetWidth, targetHeight);
using (var graphics = Graphics.FromImage(resizedImage))
{
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
var attributes = new ImageAttributes();
attributes.SetWrapMode(WrapMode.TileFlipXY);
var destination = new Rectangle(0, 0, targetWidth, targetHeight);
graphics.DrawImage(image, destination, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes);
}
return resizedImage;
}
//Edit:
Okay i think i'm gonna use Get/SetPixel or Panels(for small captures), as i did here on the right, the left is still the 80x80 Picturebox

Application C# image quality [duplicate]

This question already exists:
Application about C# image quality [closed]
Closed 2 years ago.
I am developing an application with C#. In the application I developed, I take the picture file with the user selection and transfer the picture onto the form.
After this step, I want to make changes to the bitmap of the picture. For example resizing the image in small sizes and converting to 1bpp color format.
I can do these now, I can resize the image and convert it to 1bpp color format, but at this point I think I have quality problems.
For example, when I take a screenshot of a text and send it to the program, I see that the letters in the text are unclearly bad when I view it in the resized 1bpp color format.
I show the algorithms I used and the screenshot of the application:
Bitmap bmp = new Bitmap(ResizeImage.filePath);
Bitmap bmpOriginalRGB = Helper.ImageResize(bmp, 512, 384);
pcbox1.Image = bmpOriginalRGB;
Bitmap bmpResizeRGB = Helper.ImageResize(bmp, ResizeWidth, ResizeHeight);
pcbox2.Image = bmpResizeRGB;
Bitmap bmpResize1BPP_1 = Helper.ConvertTo1BppImage(Helper.ImageResize(bmp, ResizeWidth, ResizeHeight));
pcbox3.Image = bmpResize1BPP_1;
Bitmap bmpResize1BPP_2 = bmpResizeRGB.Clone(new Rectangle(0, 0, ResizeWidth, ResizeHeight), PixelFormat.Format1bppIndexed);
pcbox4.Image = bmpResize1BPP_2;
Bitmap oledBitmap = bmpResize1BPP_2;
and
public static Bitmap ImageResize(Image image, int width, int height)
{
var destRect = new Rectangle(0, 0, width, height);
var destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
Here's a first screenshoot
Here's a second screenshoot
Here's a third screenshoot
For example, as in the second screen image, the shadow of the woman appears as black. And I don't want that. I want to see a clearer black and white image.
Do you think it is possible to further improve this picture quality?
Edit: Sorry for translate..
I don't believe your problem is the quality on resizing, it's rather the conversion from greyscale to black and white.
When resizing, the color of the adjacent pixels are averaged together to get a new color approximating the set of pixels. So going from a 200x200 pixel image to a 100x100 pixel image, a set of 4 pixels becomes a single pixel. It will still look fairly good, but the solid black text will become a series of gray pixels. The exact formula will vary by the interpolation method used. But when resizing a nice piece of black and white text, the text will end up lighter or more grey (which will be important later)
However, when going from greyscale (or full color) to black and white each pixel ends up being either black and white, there are no other options, but there are different algorithms used to decide which pixels end up black or white, often called dithering. (https://en.wikipedia.org/wiki/Dither)
Your first b/w image appears to use some form of error diffusion, quite possibly Floyd-Steinberg. It tends to work fairly well on real wold images and turns grey areas into spaced out black pixels visually approximating the greyness of the area they fall in.
Your second b/w image appears to be a simple threshold algorithm. Basically pixels darker than a certain color end up black, and all the rest end up white. You can adjust the image by simply setting which color is used as the threshold. Often this works well with text, but you will need to adjust the value used as the threshold, but I don't know if the libraries you are using allow for this or not. I have found what works well for programmatically selecting the threshold is to total up how many pixels there are of each color, and then assume some percent will be black (text tends to be mostly white space), then choose the threshold that gives you that number of black pixels.
And there are many other dithering algorithms that you can try, as well as edge detection algorithms. You can also try adjusting the contrast of the image before converting to b/w.
However, at the end of the day, when resolution is low (pixels per character), it may not be possible to easily convert them to b/w and have them still be readable (just try to fax small text on a fax machine in standard mode). Remember your resize removed a lot of information and the conversion from 8 bit to 1 bit removed another 87% of the information in the resized picture.

C# Zoomed image in Picturebox gets cropped in top left

I want to make very simplistic paint/image editor. Mainly, for pixel editing, but that doesn't seem relevant.
To ease up my effort, I decided to keep the image size at 16x16.
I populate the form, add a PixelBox and slap a default image on it.
Of course, I need to make the pixels visible, set the interpolation to NearestNeighbor.
Then, I stretch the pixelbox to 320x320. And there the situation arises.
The image is displayed as thus:
Cropped image
Could someone shed some light on this? This is just a 16x16 image with a checkerboard pattern that I made, but I can't figure out why it is displayed with that offset at the top left.
Also, no code as been yet added. I assume this is default behavior?
If you look at the examples on the page that exact same error happens, so it must be a bug on the PixelBox.
Instead of using a custom control for this type of operation just use the standard PictureBox and scale the image by yourself:
public Bitmap ScaleBitmap(Bitmap src, Size NewSize)
{
Bitmap bmp = new Bitmap(NewSize.Width, NewSize.Height, src.PixelFormat);
Graphics g = Graphics.FromImage(src);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
g.DrawImage(src, new Rectangle(Point.Empty, NewSize), new Rectangle(0, 0, src.Width, src.Height), GraphicsUnit.Pixel);
g.Dispose();
return bmp;
}

White pixels when resizing semi-transparent bitmap

Sometimes when I resize semi-transparent png's I get weird white pixels on shapes edges. This happens only with images that have shapes (not photos) and when InterpolationMode is set to HighQualityBicubic. Does anyone know why this white pixels show? I get them only on some images.
This is the result I get:
This is the source file:
This is similar file, that does not cause that "white pixel" effect.
Drawing code is very simple:
Bitmap resize = new Bitmap(1024, 177, PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(resize))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(Properties.Resources.Wave01, 0, 0, 1024, 177);
}
Please note, that I use HighQualityBicubic because the code is used for resizing various types of images including photos. Using InterpolationMode.Default seems to solve the problem, but then resizing some image types may produce worse results than HighQualityBicubic.

How to solve grayish frame issue when Scaling a bitmap using GDI+

I'm trying to scale down a Bitmap using GDI+ by doing the following:
Bitmap newImage = new Bitmap(NewWidth, NewHeight, Im.PixelFormat);
Graphics g = Graphics.FromImage(newImage);
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.ScaleTransform(0.1, 0.1); // 10%
g.DrawImage(Im, 0, 0, Im.Width, Im.Height);
Im is the original image, NewWidth and NewHeight are 10% or the original image. I've tested this on a 1000x1000 image (shrinking it down to 100x100)
The scaling is done correctly with high quality as promised but for some reason there is a gray border on the left, right and top borders (none on the bottom).
I assume this is due to the fact the all the image borders are white and the color "outside" of the bitmap is by default black so some of the default black get mixed into the scaling interpolation.
I looked for a way to set the default background color to white (white will do just fine) but couldn't find it anywhere..
My alternative is to pad the border with a white frame, scale the image down and the crop it but I was wondering if there is a simpler and less CPU consuming way?
Any ideas?
Well. After some more digging I found it..
System.Drawing.Imaging.ImageAttributes Att = new System.Drawing.Imaging.ImageAttributes();
Att.SetWrapMode(System.Drawing.Drawing2D.WrapMode.Clamp, System.Drawing.Color.White);
g.DrawImage(Im, new Rectangle(0,0,Im.Width,Im.Height), 0, 0, Im.Width, Im.Height, GraphicsUnit.Pixel, Att);
Try adding an alpha overlay with an extra transparent pixel or two on all sides. This should get a better result than using either an explicit or implied solid color frame. Though converting to RGBA and adding the frame has a higher execution cost, if you are really interested in high quality and don't want to switch graphics libraries it may be the way to go.

Categories