ASP.Net MVC Image Upload Resizing by downscaling or padding - c#

A user will be able to upload an image. If the image is greater than a set size I want to downsize it to that size. Obviously it doesn't have to match exactly due to ratios, the width would be the key size so the height would be variable.
If the image is smaller than the set size I would like to create a new image to the set size with a background of a defined colour and then centre the uploaded image into it therefore the result is the original with paddded colour.
Any code examples or links greatly appreciated

Here's a snippet of code I quickly knocked up for resizing it based on the width. I'm sure you could figure out how to add a background color to the Bitmap. It's not complete code but just an idea of how to do things.
public static void ResizeLogo(string originalFilename, string resizeFilename)
{
Image imgOriginal = Image.FromFile(originalFilename);
//pass in whatever value you want for the width (180)
Image imgActual = ScaleBySize(imgOriginal, 180);
imgActual.Save(resizeFilename);
imgActual.Dispose();
}
public static Image ScaleBySize(Image imgPhoto, int size)
{
int logoSize = size;
float sourceWidth = imgPhoto.Width;
float sourceHeight = imgPhoto.Height;
float destHeight = 0;
float destWidth = 0;
int sourceX = 0;
int sourceY = 0;
int destX = 0;
int destY = 0;
// Resize Image to have the height = logoSize/2 or width = logoSize.
// Height is greater than width, set Height = logoSize and resize width accordingly
if (sourceWidth > (2 * sourceHeight))
{
destWidth = logoSize;
destHeight = (float)(sourceHeight * logoSize / sourceWidth);
}
else
{
int h = logoSize / 2;
destHeight = h;
destWidth = (float)(sourceWidth * h / sourceHeight);
}
// Width is greater than height, set Width = logoSize and resize height accordingly
Bitmap bmPhoto = new Bitmap((int)destWidth, (int)destHeight,
PixelFormat.Format32bppPArgb);
bmPhoto.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution);
Graphics grPhoto = Graphics.FromImage(bmPhoto);
grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
grPhoto.DrawImage(imgPhoto,
new Rectangle(destX, destY, (int)destWidth, (int)destHeight),
new Rectangle(sourceX, sourceY, (int)sourceWidth, (int)sourceHeight),
GraphicsUnit.Pixel);
grPhoto.Dispose();
return bmPhoto;
}

You can just load the file into a bitmap object:
http://msdn.microsoft.com/en-us/library/system.drawing.bitmap.aspx
Then just check the width on the object. For the second part of your problem, I would recommend using a tool like ImageMagick
http://www.imagemagick.org/script/index.php
to accurately resize the first image or to create the background image and merge the two images together.

Related

Issue with horizontally resizing BMP Image (C#)

Link to Project: https://github.com/FFladenmuller/resize-bmp
Code works for a resize with a factor of 1. However, if trying a larger factor and I open the image, Photos says: "It looks like we don't support this file format".
I have not added in padding yet but I have only worked with images whose width is divisible by 4.
For loop to add BGR bytes to new Image:
for (int i = 54; i < oldBMP.Info.Count - 2; i += 3)
{
for(int j = 0; j < sizeMultiplier; j++)
{
newBMP.Info.Add(oldBMP.Info[i]);
newBMP.Info.Add(oldBMP.Info[i + 1]);
newBMP.Info.Add(oldBMP.Info[i + 2]);
}
}
First for loop to increment through BGR triples, second for loop to add each pixel sizeMultiplier amount of times.
Define the following method:
public static Bitmap ResizeImage(Bitmap image, Size size)
{
try
{
Bitmap result = new Bitmap(size.Width, size.Height);
using (Graphics g = Graphics.FromImage((Image)result))
{
g.CompositingQuality = CompositingQuality.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.DrawImage(image, 0, 0, size.Width, size.Height);
}
return result;
}
catch
{
return image;
}
}
Then, within your code, whenever a resize is needed:
Bitmap image = new Bitmap(#"C:\Path\MyImage.bmp");
Single scaleWidth = 1.2f;
Int32 targetWidth = (Int32)((Single)image.Width * scaleWidth);
Single scaleHeight = 1.0f;
Int32 targetHeight = (Int32)((Single)image.Height * scaleHeight);
Size size = new Size(targetWidth, targetHeight);
Bitmap imageResized = ResizeImage(image, size);
An alternative (that has the drawback of reducing the output quality) is the following:
Bitmap image = new Bitmap(#"C:\Path\MyImage.bmp");
Single scaleWidth = 1.2f;
Int32 targetWidth = (Int32)((Single)image.Width * scaleWidth);
Single scaleHeight = 1.0f;
Int32 targetHeight = (Int32)((Single)image.Height * scaleHeight);
Bitmap imageResized = new Bitmap(image, targetWidth, targetHeight);

Calculate Width and Height respecting Aspect Ratio for any given Max Width and Max Height

I was asked to resize any picture to its equivalent thumbnail while respecting the original aspect ratio of the picture.
So far, I've only managed to accomplish this while only passing the max. width, like follows:
public static Size GetSizeAdjustedToAspectRatio(int sourceWidth, int sourceHeight, int dWidth, int dHeight)
{
bool isLandscape = sourceWidth > sourceHeight;
int fixedSize = dWidth;
double aspectRatio = (double)sourceWidth / (double)sourceHeight; ;
if (isLandscape)
return new Size(fixedSize, (int)((fixedSize / aspectRatio) + 0.5));
else
return new Size((int)((fixedSize * aspectRatio) + 0.5), fixedSize);
}
I've tried several ways of calculating it so that it will accept any given max. height and max. width in order to keep the original aspect ratio on the end result picture.
Here:
public static Size GetSizeAdjustedToAspectRatio(int sourceWidth, int sourceHeight, int dWidth, int dHeight) {
bool isLandscape = sourceWidth > sourceHeight;
int newHeight;
int newWidth;
if (isLandscape) {
newHeight = dWidth * sourceHeight / sourceWidth;
newWidth = dWidth;
}
else {
newWidth = dHeight * sourceWidth / sourceHeight;
newHeight = dHeight;
}
return new Size(newWidth, newHeight);
}
In landscape, you set the thumbnail width to the destination box width and height is found by rule of three. In portrait, you set the thumbnail height to the destination box height and calculate width.

Graphics.DrawImage not changing size of image

I have a function which takes in an image and resizes it to fit a canvas while maintaining its aspect ratio. This code is only a minorly modified version of the code from this answer: c# Image resizing to different size while preserving aspect ratio
For this example, my canvas height is 642, my canvas width is 823.
When I run the below function, the line
graphic.DrawImage(image, posX, posY, newWidth, newHeight);
seemingly does nothing to the image size. Going in:
Image.Height == 800,
Image.Width == 1280.
newHeight = 514,
newWidth == 823
AFTER running graphic.DrawImage
Image.Height == 800,
Image.Width == 1280.
As you can see, Image's height and width are unchanged.
Does anyone see a gapingly obvious error that would cause this to happen? Thank you!
private Bitmap resizeImage(Bitmap workingImage,
int canvasWidth, int canvasHeight)
{
Image image = (Bitmap)workingImage.Clone();
System.Drawing.Image thumbnail =
new Bitmap(canvasWidth, canvasHeight);
double ratioX = (double)canvasWidth / (double)workingImage.Width;
double ratioY = (double)canvasHeight / (double)workingImage.Height;
double ratio = ratioX < ratioY ? ratioX : ratioY;
int newHeight = Convert.ToInt32((double)workingImage.Height * ratio);
int newWidth = Convert.ToInt32((double)workingImage.Width * ratio);
int posX = Convert.ToInt32((canvasWidth - ((double)workingImage.Width * ratio)) / 2);
int posY = Convert.ToInt32((canvasHeight - ((double)workingImage.Height * ratio)) / 2);
using (Graphics graphic = Graphics.FromImage(thumbnail))
{
graphic.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphic.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
graphic.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
graphic.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphic.Clear(SystemColors.Control);
graphic.DrawImage(image, posX, posY, newWidth, newHeight); //<--- HERE
}
System.Drawing.Imaging.ImageCodecInfo[] info =
System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();
System.Drawing.Imaging.EncoderParameters encoderParameters;
encoderParameters = new System.Drawing.Imaging.EncoderParameters(1);
encoderParameters.Param[0] = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality,
100L);
return workingImage;
}
Size of your image is defined here
Image image = (Bitmap)workingImage.Clone();
This
graphic.DrawImage(image, posX, posY, newWidth, newHeight);
only draws the image with specified arguments, but it does not mean that the image size gets changed. In other words, drawing an image simply does not change its size, it just takes the image and draws it on a canvas as you wish.
Image Resizing functanality pupose see the below link
http://www.codeproject.com/Articles/30524/An-Easy-to-Use-Image-Resizing-and-Cropping-Control?msg=5203911#xx5203911xx
This link content may help you

Scaling image to 1600px X 700 px

When an user uploads any image, can we scale it to 1600 X 700?
I'm using the code below to scale the images which are 1996 X 1442 , but it never scales to 1600 X 700.
Any better way or other way to achieve it?
private static Image resizeImage(Image imgToResize, Size size)
{
int sourceWidth = imgToResize.Width;
int sourceHeight = imgToResize.Height;
float nPercent = 0;
float nPercentW = 0;
float nPercentH = 0;
nPercentW = ((float)size.Width / (float)sourceWidth);
nPercentH = ((float)size.Height / (float)sourceHeight);
if (nPercentH < nPercentW)
nPercent = nPercentH;
else
nPercent = nPercentW;
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
Bitmap b = new Bitmap(destWidth, destHeight);
Graphics g = Graphics.FromImage((Image)b);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
g.Dispose();
return (Image)b;
}
If you check the comments below, they say it's not possible to archive it without loosing aspect ratio, but if I see the HERO images from airbnb they have images which are of 1600 X 700...
So I think it's somehow possible but I don't know how to achieve it...
Of course not, as 1600 x 700 has a different aspect ratio (0.4375) than 1996 x 1442 (0.7724). The way you do it is to scale keeping the aspect ratio, so you'll never get to 1600 x 700 from 1996 x 1442.
You could:
Scale the image so that the longer corner is 1600 long and then cut out a relevant part of size 1600 x 700 (keeping the aspect ratio).
Scale the image to 1600 x 700, probably distorting the image as it is vertically "squeezed".
Another problem in your code are rounding issues.
int destWidth = (int)...;
int destHeight = (int)...;
simply cuts off the decimal places. Even if the computation result would be 699.9, destWidth would be 699.
You might want to use Math.Round instead, which would round 699.9 to 700 as expected.
Well, actually yes! There is almost nothing impossible in this world. Just try to search information about "seam carving". Sorry, no code here, because I'm not so familiar with these algorithms.
I've tweaked your method in a few ways:
It now scales according to the bigger ratio, not the smaller one, so you will end up trimming bits off your image rather than leaving white(?) letterboxing. (This is just < vs > though so it's easy to change.)
For the side that should be equal, this ensures it's equal, rather than scaling and rounding and hoping you don't end up with one pixel difference.
If the aspect ratios do not match, it centers the drawn image on g so you end up with equal amounts trimmed off left and right or top and bottom. (Without knowing that the original image is in the correct ratio, you have to decide somehow which bits of the image to trim off.)
Give it a try and see if you like the results.
private static Image resizeImage(Image imgToResize, Size size)
{
int sourceWidth = imgToResize.Width;
int sourceHeight = imgToResize.Height;
double wRatio = (double)size.Width / sourceWidth;
double hRatio = (double)size.Height / sourceHeight;
int destWidth, destHeight;
float top, left;
if (wRatio > hRatio) // reverse for white bars rather than trimming
{
destWidth = size.Width;
destHeight = (int)Math.Ceiling(sourceHeight * wRatio);
left = 0;
top = (destHeight - size.Height) / 2f;
}
else
{
destHeight = size.Height;
destWidth = (int)Math.Ceiling(sourceWidth * hRatio);
top = 0;
left = (destWidth - size.Width) / 2f;
}
Bitmap b = new Bitmap(size.Width, size.Height);
Graphics g = Graphics.FromImage((Image)b);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(imgToResize, left, top, destWidth, destHeight);
g.Dispose();
return (Image)b;
}
This code preserves the Aspect Ratio of the original image. If preserving it is not important, then you can get rid of the ratio calculation part and create a Bitmap object with the size provided directly.
private static Image resizeImage(Image imgToResize, Size size)
{
Bitmap b = new Bitmap(size.Width, size.Height);
Graphics g = Graphics.FromImage((Image)b);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
g.Dispose();
return (Image)b;
}

How to Preserve aspect ratio of a button image .net

I'm trying to re-size an image of the Button control while preserving the aspect ratio from the original image so the new image doesn't look squashed/stretched.
And when i again get it back to the original position (Normal Window Maximized state) it should retain its original resolution.
Basically i want the images in all of monitor resolutions to look the same.
For ex: In my application,
An image of a Globe looks fine with the monitor resolution 1280 * 1024 (5:4)
but looks stretched like an ellipse with the monitor resolution 1920×1080 (16:9)
I know i have to do some form of transformation programatically, but am not getting how to do it. I tried calling the below method on OnResize event, though it works for the first iteration, the images gets smaller on subsequent resizing and finally i lose my image finally.
public Rectangle MaintainAspectRatio(Image imgPhoto, Rectangle thumbRect)
{
int sourceWidth = imgPhoto.Width;
int sourceHeight = imgPhoto.Height;
int sourceX = 0;
int sourceY = 0;
int destX = 0;
int destY = 0;
float nPercent = 0;
float nPercentW = 0;
float nPercentH = 0;
nPercentW = ((float)thumbRect.Width / (float)sourceWidth);
nPercentH = ((float)thumbRect.Height / (float)sourceHeight);
//if we have to pad the height pad both the top and the bottom
//with the difference between the scaled height and the desired height
if (nPercentH < nPercentW)
{
nPercent = nPercentH;
destX = (int)((thumbRect.Width - (sourceWidth * nPercent)) / 2);
}
else
{
nPercent = nPercentW;
destY = (int)((thumbRect.Height - (sourceHeight * nPercent)) / 2);
}
int destWidth = (int)(sourceWidth * nPercent);
int destHeight = (int)(sourceHeight * nPercent);
Rectangle retRect = new Rectangle(thumbRect.X, thumbRect.Y, destWidth, destHeight);
return retRect;
}
I use this code in my overriden OnPaint eventhandler method as :
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
int iHeight;
int iWidth;
if (Image != null)
{
Rectangle ResizedRectangle = MaintainAspectRatio(Image,ClientRectangle);
iHeight = ResizedRectangle.Height;
iWidth = ResizedRectangle.Width;
}
else
{
iWidth = ClientRectangle.Width;
iHeight = ClientRectangle.Height;
}
// Draw border
Color oLeftTopColor = SystemColors.ControlLightLight;
Color oRightBottomColor = SystemColors.ActiveCaption;
Pen oLeftTopPen = new Pen(oLeftTopColor);
Pen oRightBottomPen = new Pen(oRightBottomColor);
// top line
g.DrawLine(oLeftTopPen, 0, 0, iWidth - 1, 0);
//g.DrawLine(new Pen(SystemColors.ControlLight), 1, 1, iWidth-2, 1);
// bottom line
g.DrawLine(oRightBottomPen, 0, iHeight, iWidth - 1, iHeight);
//g.DrawLine(new Pen(SystemColors.ControlDark), 1, iHeight-2, iWidth-2, iHeight-2);
// right line
g.DrawLine(oRightBottomPen, iWidth, 0, iWidth, iHeight - 1);
//g.DrawLine(new Pen(SystemColors.ControlDark), iWidth-2, 1, iWidth-2, iHeight-2);
// left line
g.DrawLine(oLeftTopPen, 0, 0, 0, iHeight - 1);
//g.DrawLine(new Pen(SystemColors.ControlLightLight), 1, 1, 1, iHeight-2);
// Draw image
if (Image != null)
{
Rectangle oDrawRectagle = new Rectangle(
0, 0, iWidth, iHeight);
if (Enabled == false)
{
e.Graphics.DrawImage(Image, oDrawRectagle,
0, 0, Image.Width, Image.Height,
GraphicsUnit.Pixel);
}
else
{
e.Graphics.DrawImage(Image, oDrawRectagle);
}
}
// Draw text
if (Text != null)
{
int iTextTop = 5;
int iTextLeft = 5;
int iMaxTextWidth = iWidth - 10;
int iMaxTextHeight = iHeight - 10;
}
}
Can anyone guide me how should i proceed with this ?
I tried calling the below method on
OnResize event, though it works for
the first iteration, the images gets
smaller on subsequent resizing and
finally i lose my image finally.
I think you should use the DisplaySettingsChanged instead of OnResize event, it will trigger only when resolution is changed.
It's not very clear how you are using the image - maybe you should post the code where you actually display the image.
An image might appear deformed on different resolutions if the pixel aspect ratio (PAR) differs in the two resolutions - is this your case? Do you want to display an image independent of the PAR?
In this case you need to keep the image internally at a known DPI value (96, usually) and then stretch it with your method by the values of the current resolution's DPI, which can be obtain with something like this:
Single xDpi, yDpi;
IntPtr dc = GetDC(IntPtr.Zero);
using(Graphics g = Graphics.FromHdc(dc))
{
xDpi = g.DpiX;
yDpi = g.DpiY;
}
if (ReleaseDC(IntPtr.Zero) != 0)
{
// GetLastError and handle...
}
[DllImport("user32.dll")]
private static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("user32.dll")]
private static extern Int32 ReleaseDC(IntPtr hwnd);
Then you need to scale your image so that final width = original width * 96 / xDpi, and similar for height.

Categories

Resources