Reputation: 721
I am developing application using .NET(4.5) MVC(4.0) C#(5.0). i want to generate image thumbnail from image that i already have. Now requirement is like it should generate thumbnail of maximum square portion from center of image without stretching not whole image except image is square size.
as per example my original image size :578x700 i want to generate thumbnail for placeholder size :200x150, 185x138, 140x140, 89x66, 80x80, 45x45, 28x28
i had create my below code but didn't get exact result. here is my core method that generate thumbnail
public string GenerateThumbnailFromImage(string imageFilePath, int thumbWidth, int thumbHeight)
{
try
{
//Check if file exist
if (File.Exists(imageFilePath))
{
//bool preserveAspectRatio = true;
string oldFilePath = imageFilePath;
string folderPath = Path.GetDirectoryName(imageFilePath);
string filename = Path.GetFileNameWithoutExtension(imageFilePath);
//Rename file with thumbnail size
filename = filename + "_" + thumbWidth.ToString() + Path.GetExtension(imageFilePath);
imageFilePath = Path.Combine(folderPath, filename);
using (Image image = Image.FromFile(oldFilePath))
{
decimal originalWidth = image.Width;
decimal originalHeight = image.Height;
decimal requiredThumbWidth = thumbWidth;
decimal requiredThumbHeight = thumbHeight;
decimal startXPosition = 0;
decimal startYPosition = 0;
decimal screenWidth = originalWidth;
decimal screenHeight = originalHeight;
decimal ar = thumbWidth < thumbHeight
? originalWidth / originalHeight
: originalHeight / originalWidth;
//Define Starting Position for thumbnail generation
if (originalWidth > originalHeight)
startXPosition = (originalWidth - originalHeight) / 2;
else if (originalHeight > originalWidth)
startYPosition = (originalHeight - originalWidth) / 2;
if (thumbWidth>thumbHeight)
{
requiredThumbWidth = thumbWidth;
requiredThumbHeight = requiredThumbWidth*ar;
}
else if (thumbHeight>thumbWidth)
{
requiredThumbHeight = thumbHeight;
requiredThumbWidth = requiredThumbHeight*ar;
}
else
{
requiredThumbWidth = thumbWidth;
requiredThumbHeight = thumbWidth;
}
using (var bmp = new Bitmap((int)requiredThumbWidth, (int)requiredThumbHeight))
{
Graphics gr = Graphics.FromImage(bmp);
gr.SmoothingMode = SmoothingMode.HighQuality;
gr.CompositingQuality = CompositingQuality.HighQuality;
gr.InterpolationMode = InterpolationMode.High;
var rectDestination = new Rectangle(0, 0, (int)requiredThumbWidth, (int)requiredThumbHeight);
gr.DrawImage(image, rectDestination, (int)startXPosition, (int)startYPosition, (int)screenWidth, (int)screenHeight, GraphicsUnit.Pixel);
bmp.Save(imageFilePath);
return filename;
}
}
}
return null;
}
catch (Exception ex)
{
GlobalUtil.HandleAndLogException(ex, this);
throw ex;
}
finally
{
}
}
Upvotes: 7
Views: 17448
Reputation: 1914
I am using SkiaSharp. But it's not that different if you use other systems.
Enter widthIn and heightIn as your desired thumbnail's sides.
public async Task<string?> CropImageAndSave(string filePath, int widthIn, int heightIn)
{
// get the image
using SKImage sKImage = SKImage.FromEncodedData(filePath);
// check if the given sides are not larger than the image size
if (widthIn < sKImage.Width && heightIn < sKImage.Height)
{
// find the center
int centerX = sKImage.Width / 2;
int centerY = sKImage.Height / 2;
// find the start points
int startX = centerX - widthIn / 2;
int startY = centerY - heightIn / 2;
// crop the image
SKImage croppedImage = sKImage.Subset(SKRectI.Create(startX, startY, widthIn, heightIn));
using SKData sKData = croppedImage.Encode(SKEncodedImageFormat.Jpeg, 100);
// (await) SAVE the image in your desired location
// then
return $"Your image is cropped; Width: {widthIn}, Height: {heightIn}";
}
else return null;
}
Upvotes: 0
Reputation: 41
Also using the ImageFactory NuGet package, I recommend using the Resize Crop feature.
See examples of this here
using ImageProcessor;
using ImageProcessor.Imaging;
using System.Drawing;
using System.IO;
public static byte[] ResizeAndCrop(byte[] image, int width, int height)
{
using (var ms = new MemoryStream())
{
using (var imgf = new ImageFactory(true))
imgf
.Load(image)
.Resize(new ResizeLayer(new Size(width, height), ResizeMode.Crop))
.Save(ms);
return ms.ToArray();
}
}
This will take any image in its byte[]
format and crop from the center.
This is set by the anchorPosition parameter in the ResizeLayer which has a deafult value of AnchorPosition.Center
new ResizeLayer(new Size(width, height), ResizeMode.Crop/*, AnchorPosition.Center*/)
Upvotes: 3
Reputation: 326
In your NuGet package, add this ImageFactory solution by James South. Everything you need is there. I wish I could buy James more beer.
Sample usage:
using (ImageFactory imgf = new ImageFactory(preserveExifData: true)) {
imgf
.Load(img)
.Crop(rect)
.Format(new JpegFormat { Quality = 100 })
.Save(destination)
}
// given that :
// 'img' is your Image object, could be an Image.FromFile() object or the likes
// 'rect' is the size of your crop and that you have already did the math
// new JpegFormat { Quality = 70 } is part of the package
// and 'destination' is the destination path of your new image in your disk
Upvotes: 4
Reputation: 341
public Image ScaleImage(Image image, int maxWidth, int maxHeight)
{
var ratioX = (double)maxWidth / image.Width;
var ratioY = (double)maxHeight / image.Height;
var ratio = Math.Min(ratioX, ratioY);
var newWidth = (int)(image.Width * ratio);
var newHeight = (int)(image.Height * ratio);
var newImage = new Bitmap(maxWidth, maxWidth);
using (var graphics = Graphics.FromImage(newImage))
{
// Calculate x and y which center the image
int y = (maxHeight/2) - newHeight / 2;
int x = (maxWidth / 2) - newWidth / 2;
// Draw image on x and y with newWidth and newHeight
graphics.DrawImage(image, x, y, newWidth, newHeight);
}
return newImage;
}
Upvotes: 3
Reputation: 5248
You need to get the ratio of the destination size against the actual size. Scale the shorter side until it touches the actual image size. Crop it starting from the center and scale it to the desired size.
Here is the code:
public static Image ResizeImage(Image imgToResize, Size destinationSize)
{
var originalWidth = imgToResize.Width;
var originalHeight = imgToResize.Height;
//how many units are there to make the original length
var hRatio = (float)originalHeight/destinationSize.Height;
var wRatio = (float)originalWidth/destinationSize.Width;
//get the shorter side
var ratio = Math.Min(hRatio, wRatio);
var hScale = Convert.ToInt32(destinationSize.Height * ratio);
var wScale = Convert.ToInt32(destinationSize.Width * ratio);
//start cropping from the center
var startX = (originalWidth - wScale)/2;
var startY = (originalHeight - hScale)/2;
//crop the image from the specified location and size
var sourceRectangle = new Rectangle(startX, startY, wScale, hScale);
//the future size of the image
var bitmap = new Bitmap(destinationSize.Width, destinationSize.Height);
//fill-in the whole bitmap
var destinationRectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
//generate the new image
using (var g = Graphics.FromImage(bitmap))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(imgToResize, destinationRectangle, sourceRectangle, GraphicsUnit.Pixel);
}
return bitmap;
}
Call it like this:
var thumbImage = ImageHelper.ResizeImage(image, new Size(45, 45));
thumbImage.Save(thumbFullPath);
Upvotes: 18
Reputation: 159
Try this:
bool SaveCroppedImage(Image image, int targetWidth, int targetHeight, string filePath)
{
ImageCodecInfo jpgInfo = ImageCodecInfo.GetImageEncoders().Where(codecInfo => codecInfo.MimeType == "image/jpeg").First();
Image finalImage = image;
System.Drawing.Bitmap bitmap = null;
try
{
int left = 0;
int top = 0;
int srcWidth = targetWidth;
int srcHeight = targetHeight;
bitmap = new System.Drawing.Bitmap(targetWidth, targetHeight);
double croppedHeightToWidth = (double)targetHeight / targetWidth;
double croppedWidthToHeight = (double)targetWidth / targetHeight;
if (image.Width > image.Height)
{
srcWidth = (int)(Math.Round(image.Height * croppedWidthToHeight));
if (srcWidth < image.Width)
{
srcHeight = image.Height;
left = (image.Width - srcWidth) / 2;
}
else
{
srcHeight = (int)Math.Round(image.Height * ((double)image.Width / srcWidth));
srcWidth = image.Width;
top = (image.Height - srcHeight) / 2;
}
}
else
{
srcHeight = (int)(Math.Round(image.Width * croppedHeightToWidth));
if (srcHeight < image.Height)
{
srcWidth = image.Width;
top = (image.Height - srcHeight) / 2;
}
else
{
srcWidth = (int)Math.Round(image.Width * ((double)image.Height / srcHeight));
srcHeight = image.Height;
left = (image.Width - srcWidth) / 2;
}
}
using (Graphics g = Graphics.FromImage(bitmap))
{
g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(image, new Rectangle(0, 0, bitmap.Width, bitmap.Height), new Rectangle(left, top, srcWidth, srcHeight), GraphicsUnit.Pixel);
}
finalImage = bitmap;
}
catch { }
try
{
using (EncoderParameters encParams = new EncoderParameters(1))
{
encParams.Param[0] = new EncoderParameter(Encoder.Quality, (long)100);
//quality should be in the range [0..100] .. 100 for max, 0 for min (0 best compression)
finalImage.Save(filePath, jpgInfo, encParams);
return true;
}
}
catch { }
if (bitmap != null)
{
bitmap.Dispose();
}
return false;
}
Upvotes: 8
Reputation: 1055
Done this a few times before, the trick is to fit the Height of the image first, the rescale the Width to the proportion that you had to reduce the Height, then repeat from the Width if it still doesnt fit by the Width, and reducing the newer scaled Height by that additional proportion. That way you have a thumbnail that always fits, possibly some whitespace in the X or Y, but the image is still to the same proportions, not stretched.
int originalHeight;
int originalWidth;
int imageHeight;
int imageWidth;
int requiredHeight;
int requiredWidth;
double scale;
if(originalHeight > requiredHeight)
{
scale = requiredHeight / originalHeight;
imageHeight = requiredHeight;
imageWidth = originalHeight * scale;
}
if(imageWidth > requiredWidth)
{
scale = requiredWidth / imageWidth;
imageWidth = requiredWidth;
imageHeight = imageHeight * scale;
}
And then drawing that Image
into a new Bitmap
of this size using Graphics
object
Upvotes: 1