Don't scale image when creating new bitmap from existing image - c#

Does anyone know how to create a new bitmap from an existing image with a taller height, but don't scale the image and just have transparent, black or white below the original image in the new bitmap?
I basically have one picture that is taller than the second and I need the second one to be as tall as the first, without stretching it.
img2 = new Bitmap(lImages[2],new Size(pictureBox.Image.Width,pictureBox.Image.Height));
img2 = ((Bitmap)img2).Clone(new Rectangle(0, 0, pictureBox.Image.Width, pictureBox.Image.Height), System.Drawing.Imaging.PixelFormat.Format24bppRgb);
C# .NET 4.0.

By using a Graphics object, you can achieve this easily:
Bitmap temp = new Bitmap(new Size(pictureBox.Image.Width,pictureBox.Image.Height));
using(Graphics g = Graphics.FromImage(temp))
{
g.DrawImage(img2, 0, 0);
}
img2 = temp;
Now img2 references a new Bitmap object of the required size which has the original (unstretched) image painted on it.
Note: To control the color of the extra space, add a call to g.FillRect before drawing the image.

Create your "standart" size bitmap and fill it with, let's say, white color and call Bitmap.MakeTransparent(Color.White) and draw your final image over it.

Related

Graphics is blank when trying to turn anti-aliasing off for a bitmap (C#)

What I'm trying to do:
Since in my bitmaps there are some unwanted white edges around the picture that result from anti-aliasing as pointed out from another user from stackoverflow.
I'm trying to convert an image that's inputted into a bitmap, convert bitmap into a Graphics object so that I can set the Smooth Mode to none, and then finally convert that Graphics object to a bitmap so that it can be copied by the user after setting it to the clipboard. I'm not sure if this is a good way of getting rid anti-aliasing in bitmaps but I'm definitely interested in improvements and suggestions.
The issue I'm facing:
The result of the image after is completely blank and does not contain any of the pixels that are previously found in the original bitmap. Here's the result:
This issue applies to all pictures no matter what their format is.
My code:
public PicGen(PictureBox pictureBox)
{
Clipboard.Clear();
Bitmap firstImage = new(pictureBox.Image, pictureBox.Width, pictureBox.Height);
RectangleF cloneRect = new RectangleF(0, 0, firstImage.Width, firstImage.Height);
System.Drawing.Imaging.PixelFormat format = firstImage.PixelFormat;
Bitmap cloneBitmap = firstImage.Clone(cloneRect, format);
Graphics AntiARemover = Graphics.FromImage(cloneBitmap);
AntiARemover.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
Bitmap finalImg = new(52, 52, AntiARemover);
Clipboard.SetImage(finalImg);
Color backColorBottom = firstImage.GetPixel(0, 0);
firstImage.ReplaceColor(backColorBottom, Color.FromArgb(54, 57, 63));
Bitmap finalImg = new(52, 52, AntiARemover);
From the documentation for this bitmap constructor:
The new Bitmap that this method creates takes its horizontal and vertical resolution from the DpiX and DpiY properties of g, respectively.
If you want create a new image with the content from another you need to call one of the DrawImage methods. You should also dispose your graphics object, and any temporary bitmaps you may use.
using var finalImg = new Bitmap(52,52);
using var graphics = Graphics.FromImage(finalImg);
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
graphics.DrawImage(cloneBitmap)
However, edge artifacts typically occur when combining two images using an alpha channel, see Premultiplied alpha. In your example I can only see one input image, so I'm really not sure what it is you are actually trying to do. If you need to convert to premultiplied alpha you can use the following code to convert the color for each pixel
premultiplied.R = (byte)(straight.R * straight.A / 255);
premultiplied.G = (byte)(straight.G * straight.A / 255);
premultiplied.B = (byte)(straight.B * straight.A / 255);
premultiplied.A = straight.A;

How to get a pixel color from a screen with the defined max color depth?

Clickermann has function ColorMode(n).
Functionality:
The procedure modifies the current screenshot by applying a filter to it that reduces the number of colors in the palette
I want do the same thing with C#.
I need to get a pixel color from screen according to max color depth because it must work with regions with a lot of similar colors (for example gradient)
// Creates an empty bitmap with the size of the current screen
Bitmap screenshotBitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
// Creates a new graphics object that can capture the screen
Graphics graphics = Graphics.FromImage(screenshotBitmap);
// Draw a screenshot on the bitmap
graphics.CopyFromScreen(0, 0, 0, 0, screenshotBitmap.Size);
// Clone bitmap and make it 4 bit
Rectangle screenshotBitmapRectangle = new Rectangle(0, 0, screenshotBitmap.Width, screenshotBitmap.Height);
Bitmap screenshotBitmap4BitPerPixel = screenshotBitmap.Clone(screenshotBitmapRectangle, PixelFormat.Format4bppIndexed);
And do everything you want with bitmap
screenshotBitmap4BitPerPixel.Save(#"C:\Users\FreePhoenix\Desktop\Bitmap.bmp");

C# GDI+ ScaleTransform ok on picturebox but image saved is original

Hi I have the issue that when I use ScaleTransform(zoomFactor,zoomFactor) the image saved on disk is the original version always, while on screen in the picturebox the image is distorted in proportion to the zoomFactor.
Why this could be happening ? Shouldn't I have the final result as applied from e.Graphics on disk written image ?
My code is the following which is a version with matrix. but the instead of matrix I have used the ScaleTransform as well. Result is always the same:
g=e.Graphics;//inside picturebox_paint()
g.ScaleTransform(ratio * zoomFac, ratio * zoomFac);
e.Graphics.DrawImage((Bitmap)bmp, 0, 0);
int seed = Convert.ToInt32(Regex.Match(Guid.NewGuid().ToString(), #"\d+").Value);
String destinationFile = #"C:\tmp\photoid\" + new Random(seed).Next() + "_conv.jpg";
//Here I get always the original image back!!!!
bmp.Save(destinationFile);
I have used as well the following idiom but with same results:
//Matrix matrix = new Matrix();
//matrix.Scale(zoomFac, zoomFac);
//e.Graphics.Transform = matrix;
You need to make the PictureBox draw the things it shows on screen into a new Bitmap, which you then can save!
As it is the Image will be saved in the original form and nothing you did in the Paint event, which actually painst onto the surface of the PictureBox will be saved.
So to save everything, i.e. The Image, possibly a BackgroundImage and all you draw in the Paint event you would call DrawToBitmap somehwere.
Somewhere means somewhere else, not in the Paint event, as it will call the Paint event to create the new Bitmap, causing an endless loop..
To call it you would do something like this:
Bitmap bmpSave = new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
pictureBox1.DrawToBitmap(bmpSave, pictureBox1.ClientRectangle);
But maybe this is not really what you want? Maybe you actually want to modify the Image? In that case do not use the Paint event at all!
Instead do something like this:
Bitmap bmpSave = new Bitmap(yourNewWidth, yourNewHeight);
using (Graphics g = Graphics.FromImage(bmpSave))
{
g.ScaleTransform(ratio * zoomFac, ratio * zoomFac);
g.DrawImage((Bitmap)pictureBox1.Image, 0, 0); //
pictureBox1.Image = bmpSave;
bmpSave.Save(...);
}
You could call this from somewhere where the scaling is being triggered from.
Note that doing the scaling repeatedly and each time from the previoulsy scaled version will degrade the quality rather fast. For this always scale from a saved version of the original!!
Btw: Using a Matrix for scaling doesn't really make a difference over ScaleTransform.
But if you want to do a direct scaling why not use the DrawImage overload which takes two Rectangles? This is the most common solution if all you want to to scale and maybe draw other stuff additionally..:
int newWidth = 100; int newHeight = 100; string yourFileName = "D:\\xyz123.jpg";
Bitmap bmpSave = new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
Rectangle newRectangle = new Rectangle(0, 0, newWidth, newHeight);
Rectangle oldRectangle = new Rectangle(Point.Empty, pictureBox1.Image.Size);
using (Graphics g = Graphics.FromImage(bmpSave))
{
g.DrawImage((Bitmap)pictureBox1.Image, newRectangle, oldRectangle, GraphicsUnit.Pixel);
bmpSave.Save(yourFileName, ImageFormat.Jpeg);
}
And there there is the scaling Bitmap constructor:
Bitmap bmp = new Bitmap(pictureBox1.Image, newWidth, newHeight);
Which I would recommend if all you want is to scale the Image. As the other solutions it will not change the Image displayed until you assign it back into the PictureBox..:
pictureBox1.Image = bmp ;
Don't forget to dispose of the old Image..
Been a while since I messed with GDI but I think you need to copy back to the Bitmap here.
g.DrawImage(bmp, scaledwidth, scaledheight);
Try something like that before bmp.Save
Edit
Apologies for not seeing that you were copying back to the bitmap. Perhaps the overload which specifies the output rectangle is what you need. Try a DrawImage overload which has the destination Rect. https://msdn.microsoft.com/en-us/library/ms142040(v=vs.110).aspx

What's the best way to clip a bitmap using its inscribed ellipse

I'd like to take a bitmap with an ARGB 32 pixel format and clip it so that the contents within its inscribed ellipse remain, and anything outside the ellipse turns into ARGB(0,0,0,0).
I could do it programmatically using GetPixel and SetPixel and some trigonometry to figure out which pixel is out of bounds - but I suspect there's a better, more built-in way to do it.
Any ideas?
Thanks to Alessandro D'Andria for pointing out the region part - I've figured out the rest:
public Bitmap Rasterize()
{
Bitmap ringBmp = new Bitmap(width: _size.Width, height: _size.Height, format: PixelFormat.Format32bppArgb);
//Create an appropriate region from the inscribed ellipse
Drawing2D.GraphicsPath graphicsEllipsePath = new Drawing2D.GraphicsPath();
graphicsEllipsePath.AddEllipse(0, 0, _size.Width, _size.Height);
Region ellipseRegion = new Region(graphicsEllipsePath);
//Create a graphics object from our new bitmap
Graphics gfx = Graphics.FromImage(ringBmp);
//Draw a resized version of our image to our new bitmap while using the highest quality interpolation and within the defined ellipse region
gfx.InterpolationMode = Drawing2D.InterpolationMode.NearestNeighbor;
gfx.SmoothingMode = Drawing2D.SmoothingMode.HighQuality;
gfx.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality;
gfx.PageUnit = GraphicsUnit.Pixel;
gfx.Clear(Color.Transparent);
gfx.Clip = ellipseRegion;
gfx.DrawImage(image: _image, rect: new Rectangle(0, 0, _size.Width, _size.Height));
//Dispose our graphics
gfx.Dispose();
//return the resultant bitmap
return ringBmp;
}
Apparently it is extremely important to set PixelOffsetMode to HighQuality, because otherwise the DrawImage method would crop parts of the resulting image.

How can i detect black circles with AForge (IsCircle Function) since it need an image with black backgroud?

I have an image of Go board with some stones. I need to detect these stone. I was trying AForge's IsCircle Function but image's background have to be turn to black first. But I need to detect black circle.
Is it possible? or is there any other solution with other library (OpenCV)?
You can convert black pixel to white pixel and white pixel to black pixel and then use Iscircle function to detect circle here is code to convert using Aforge.net
Bitmap orig = (Bitmap)pictureBox1.Image;
Bitmap clone = new Bitmap(orig.Width, orig.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(clone))
{
gr.DrawImage(orig, new Rectangle(0, 0, clone.Width, clone.Height));
}
FiltersSequence commonSeq = new FiltersSequence();
commonSeq.Add(Grayscale.CommonAlgorithms.BT709);
commonSeq.Add(new BradleyLocalThresholding());
commonSeq.Add(new DifferenceEdgeDetector());
Bitmap temp = commonSeq.Apply(clone);

Categories