C#: Image resize and crop at the same time - c#

I'm trying to write a very fast method for creating thumbnails. It will crop and resize an image at the same time. The result image will always be square-like. So having that in mind we can now look at my code :) It is working (as far as I can see):
public static void CreateAndSaveThumbnail(string path, string output, int desiredSize)
{
using (MemoryStream ms = new MemoryStream(File.ReadAllBytes(path)))
using (Bitmap src = new Bitmap(ms))
{
Rectangle crop = src.Width > src.Height ?
new Rectangle((src.Width - src.Height) / 2, 0, src.Height, src.Height) :
new Rectangle(0, 0, src.Width, src.Width);
int size = Math.Min(desiredSize, crop.Width);
using (Bitmap cropped = src.Clone(crop, src.PixelFormat))
using (Bitmap resized = new Bitmap(size, size))
using (Graphics g = Graphics.FromImage(resized))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(cropped, 0, 0, size, size);
resized.Save(output, ImageFormat.Jpeg);
}
}
}
So how the code above works? It crops the middle part for landscape images and top part for portrait images. So this is a standard behavior. Then it resizes the cropped image (it is already a square).
Is this code OK? Could it be faster? How? Any optimizations possible?

Your routine works great but for one slight problem, at least in my desired application. I wanted the routine to crop portrait bitmaps the same as you do for landscape bitmaps; by cropping off half of the excess from the top and half of excess from the bitmap. So I simply duplicated the landscape crop code to the portrait crop code and adjusted as necessary resulting in this:
Rectangle crop = src.Width > src.Height ?
new Rectangle((src.Width - src.Height) / 2, 0, src.Height, src.Height) :
new Rectangle(0, (src.Height - src.Width) / 2, src.Width, src.Width);

Related

size of bitmap in drawImage

I've asked question on proper scaling without rim on right and bottom. link
I've checked and size of bitmap is 32 by 32 pixels.
When i try to draw it on pictureBox with drawImage(Image, int, int) it draws it little bit bigger than size of bitmap like 36 by 36 or 40 by 40. Not sure.
But when i add width and height to drawImage it draws it 32 by 32. Is this supposed to happend.
Edit:
internal void draw(Graphics g, int x, int y)
{
int s = Game.scale;
Bitmap resized = (s > 1) ? ResizeImage(image, width*s, height*s) : image;
g.DrawImage(resized, x * width * s, y * height * s);
}
private static Bitmap ResizeImage(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;
}
Feature, not a bug. If you don't specify the target size then DrawImage() pays attention to the physical size of the image. The size it had when it was created, in inches. Something you can see back in the debugger, look at the bitmap's HorizontalResolution and VerticalResolution properties.
With the diagnostic that it is probably 120 dots-per-inch, programmers commonly run their video adapter at 125% today. So the drawn image becomes 1.25 * 32 = 40 pixels.
Keep in mind that this kind of rescaling tends to be important, such an image can easily turn into but a fleck of dust on an upscale 4K monitor. They are getting pretty affordable today. Whether you want this or not depends on how the rest of your UI rescales. Check out this post about dpiAwareness in a Winforms app.

How to crop image into a square

Hi I'm trying to crop images that users upload onto my site into a square. I have already tried a few solutions posted on this website, namely http://stackoverflow.com/questions/5222711/image-resize-in-c-sharp-algorith-to-determine-resize-dimensions-height-and-wi and WebImage Crop To Square. However, although these solutions convert the image into a square, they add large areas of transparency on the top and bottom of the image, which is not what I want since these images will be used as profile images.
This is the code I have always used in my websites:
public Bitmap MakeSquarePhoto(Bitmap bmp, int size)
{
try
{
Bitmap res = new Bitmap(size, size);
Graphics g = Graphics.FromImage(res);
g.FillRectangle(new SolidBrush(Color.White), 0, 0, size, size);
int t = 0, l = 0;
if (bmp.Height > bmp.Width)
t = (bmp.Height - bmp.Width) / 2;
else
l = (bmp.Width - bmp.Height) / 2;
g.DrawImage(bmp, new Rectangle(0, 0, size, size), new Rectangle(l, t, bmp.Width - l * 2, bmp.Height - t * 2), GraphicsUnit.Pixel);
return res;
}
catch { }
}
To crop an image without adding large patches of transparency to the top and bottom, you are going to have to cut off part of the sides. Without seeing the code it should be something along the lines of cutting off (width-height)/2 pixels from each side.

Cropping Image(From Screen)

I have read many question about this. But my question is little bit different. What i need to do crop image from screen.
There is my codes
Bitmap photo = new Bitmap(Screen.PrimaryScreen.Bounds.Width,Screen.primaryScreen.Bounds.Height)
Graphics gr = Graphics.FromImage(photo);
gr.CopyFromScreen(0,0,0,0 new size(foto.Width,foto.Height));
picturebox1.Image = photo;
And there my crop codes
Rectangle cropRec = new Rectangle(1,1,1,1);
Bitmap target = new Bitmap(cropRec.Width,cropRec.Height);
using(Graphics g = Graphics.FromImage(target))
{
g.DrawImage(photo,new Rentangle(0,0,target.Width,target.Height),cropRec,GraphicsUnit.Pixel);
}
I want to crop middle part of this photo and compare with itself.
Thanks n advance
To symmetrically crop your image as shown in the following sketch, try creating a Bitmap with margins in the X and Y axis and then cropping the screenshot image by using those margins in CopyFromScreen():
Bitmap printscreen = new Bitmap(Screen.PrimaryScreen.Bounds.Width - 2 * xmargin, Screen.PrimaryScreen.Bounds.Height - 2 * ymargin);
Graphics graphics = Graphics.FromImage(printscreen as Image);
graphics.CopyFromScreen(xmargin, ymargin, 0, 0, printscreen.Size);

Image resizing with GDI+

I'm really trying to nail out a little more performance out of this tidbit of code. It's not a heavly used bit of code but is used every time a new image is uploaded, and 4 times for each image (100px, 200px, 500px, 700px). So when there are any more than 2 or 3 images processing, it gets a little busy on the server. Also I'm trying to figure out how to make it correctly process images with a low resolution. Currently it just chops it off half way through, not plesent.
Examples: Original, large, xLarge
public static byte[] ResizeImageFile(byte[] imageFile, int targetSize)
{
using (System.Drawing.Image oldImage = System.Drawing.Image.FromStream(new MemoryStream(imageFile)))
{
Size newSize = CalculateDimensions(oldImage.Size, targetSize);
using (Bitmap newImage = new Bitmap(newSize.Width, newSize.Height, PixelFormat.Format32bppRgb))
{
newImage.SetResolution(oldImage.HorizontalResolution, oldImage.VerticalResolution);
using (Graphics canvas = Graphics.FromImage(newImage))
{
canvas.SmoothingMode = SmoothingMode.AntiAlias;
canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
canvas.PixelOffsetMode = PixelOffsetMode.HighQuality;
canvas.DrawImage(oldImage, new Rectangle(new Point(0, 0), newSize));
MemoryStream m = new MemoryStream();
newImage.Save(m, ImageFormat.Jpeg);
return m.GetBuffer();
}
}
}
}
private static Size CalculateDimensions(Size oldSize, int targetSize)
{
Size newSize = new Size();
if (oldSize.Width > oldSize.Height)
{
newSize.Width = targetSize;
newSize.Height = (int)(oldSize.Height * (float)targetSize / (float)oldSize.Width);
}
else
{
newSize.Width = (int)(oldSize.Width * (float)targetSize / (float)oldSize.Height);
newSize.Height = targetSize;
}
return newSize;
}
Thanks for and help!
The first thought that comes to mind is, have you thought about Multithreading it? i.e. calling this method for each image (or batch of images) in a separate thread? That way, if your server has a few cores you can get things done quicker. Just a thought...
(Threading is a great tip.)
Try to call your method with the smallest possible image as input each time, instead of the original image. If the original image is, say 2000px, then create the 700px image from it and then use your newly created 700px image to create the 500px, etc...
With the HighQualityBicubic setting I doubt that you'll notice any difference in the 100px image. (But it of course it needs to be verified.)
For completeness, here is the solution to the second part of the question which was never answered. When processing a low resolution image the image was being cut off. The solution now, seems obvious. The problem lies in this bit of code from above:
using (Bitmap newImage = new Bitmap(newSize.Width, newSize.Height,
PixelFormat.Format32bppRgb))
The problem being that I'm selecting the PixelFormat, not letting it be the format of the original image. The correct code is here:
public static byte[] ResizeImageFile(byte[] imageFile, int targetSize)
{
using (System.Drawing.Image oldImage = System.Drawing.Image.FromStream(new MemoryStream(imageFile)))
{
Size newSize = CalculateDimensions(oldImage.Size, targetSize);
using (Bitmap newImage = new Bitmap(newSize.Width, newSize.Height,
oldImage.PixelFormat))
{
newImage.SetResolution(oldImage.HorizontalResolution,
oldImage.VerticalResolution);
using (Graphics canvas = Graphics.FromImage(newImage))
{
canvas.SmoothingMode = SmoothingMode.AntiAlias;
canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
canvas.PixelOffsetMode = PixelOffsetMode.HighQuality;
canvas.DrawImage(oldImage, new Rectangle(new Point(0, 0), newSize));
MemoryStream m = new MemoryStream();
newImage.Save(m, ImageFormat.Jpeg);
return m.GetBuffer();
}
}
}
}

generating jpg thumbnails in dotnet

i am using this code to take a jpg image and save it as a thumbnail but it seems very slow ..
Image thumbNail = image.GetThumbnailImage(width, height, null, new IntPtr());
is there any faster way to generate a large amount of thumbnails from a directory of images?
Try Draw Image - Re Edited
public Image ResizeImage(Image openImage, int NewWidth, int NewHeight) {
var openBitmap = new Bitmap(openImage);
var newBitmap = new Bitmap(NewWidth, NewHeight);
using (Graphics g = Graphics.FromImage(openBitmap))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.DrawImage(newBitmap, new Rectangle(0, 0, NewWidth, NewHeight));
}
openBitmap.Dispose(); //Clear The Old Large Bitmap From Memory
return (Image)newBitmap;
}
Typical 3-4mb Image Takes Between 4-8ms
1) By far the fastest and most reliable way to create .Jpg thumbnails is to use partial decompression.
Jpg's have a unique aspect in that it's possible to extract a 1/8 size or 1/16 size (or any power of 2 size) copy of the original without decompressing or sampling the entire original image.
Ever notice how programs like Picassa or Windows Explorer seem to create thumbnails super fast? This is how they do it (when they are not already cached).
This functionality is easily available in any library based on the Independent JPEG Group library code, and most of them are. For example ImageMagick which has a .NET layer available.
2) You can further increase speed by using a hardware accelerated library like libjpeg turbo, although it may require interop.
3) Some explanation of this special .jpg feature is here.
Try it:
public bool GenerateThumbNail(string fileName, string thumbNailFileName,
ImageFormat format, int height, int width)
{
try
{
using (Image img = Image.FromFile(fileName))
{
Image thumbNail = new Bitmap(width, height, img.PixelFormat);
Graphics g = Graphics.FromImage(thumbNail);
g.CompositingQuality = CompositingQuality.HighQuality;
g.SmoothingMode = SmoothingMode.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
Rectangle rect = new Rectangle(0, 0, width, height);
g.DrawImage(img, rect);
thumbNail.Save(thumbNailFileName, format);
}
return true;
}
catch (Exception)
{
return false;
}
}
It uses DrawImage too.

Categories