I have a method that takes an image and resizes it and saves it preserving the exif information. What I want to do now is overlay a transparent PNG image on top of the image as a watermark. The size of the png will always be larger than any of the images I want to place it on. I would like to center it on top of the image preserving the watermark's aspect ratio. Here is the code as I have it so far:
private static void ResizeImage(Image theImage, int newSize, string savePath, IEnumerable<PropertyItem> propertyItems)
{
int width;
int height;
CalculateNewRatio(theImage.Width, theImage.Height, newSize, out width, out height);
using (var b = new Bitmap(width, height))
{
using (var g = Graphics.FromImage(b))
{
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
using(var a = Image.FromFile("Watermark.png"))
{
g.DrawImage(); //What to do here?
}
g.DrawImage(theImage, new Rectangle(0, 0, width, height));
var qualityParam = new EncoderParameter(Encoder.Quality, 80L);
var codecs = ImageCodecInfo.GetImageEncoders();
var jpegCodec = codecs.FirstOrDefault(t => t.MimeType == "image/jpeg");
var encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = qualityParam;
foreach(var item in propertyItems)
{
b.SetPropertyItem(item);
}
b.Save(savePath, jpegCodec, encoderParams);
}
}
}
I figured out the solution, the code is below. May not be the optimal code but it is fast and does what I need it to do which is take all JPG images in a directory and re-size them to full and thumb images for a photo gallery while overlaying a watermark on the image.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace ImageResize
{
internal class Program
{
private static readonly string directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
private static void Main()
{
var strFiles = Directory.GetFiles(directory, "*.jpg");
//Using parallel processing for performance
Parallel.ForEach(strFiles, strFile =>
{
using (var image = Image.FromFile(strFile, true))
{
var exif = image.PropertyItems;
var b = directory + "\\" + Path.GetFileNameWithoutExtension(strFile);
ResizeImage(image, 800, b + "_FULL.jpg", exif);
ResizeImage(image, 200, b + "_THUMB.jpg", exif);
}
File.Delete(strFile);
});
}
private static void ResizeImage(Image theImage, int newSize, string savePath, IEnumerable<PropertyItem> propertyItems)
{
try
{
int width;
int height;
CalculateNewRatio(theImage.Width, theImage.Height, newSize, out width, out height);
using (var b = new Bitmap(width, height))
{
using (var g = Graphics.FromImage(b))
{
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.DrawImage(theImage, new Rectangle(0, 0, width, height));
//Using FileStream to avoid lock issues because of the parallel processing
using (var stream = new FileStream(directory + "\\Watermark.png", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (var overLay = Image.FromStream(stream))
{
stream.Close();
int newWidth;
int newHeight;
CalculateNewRatio(overLay.Width, overLay.Height, height > width ? width : newSize, out newWidth, out newHeight);
var x = (b.Width - newWidth) / 2;
var y = (b.Height - newHeight) / 2;
g.DrawImage(overLay, new Rectangle(x, y, newWidth, newHeight));
}
}
var qualityParam = new EncoderParameter(Encoder.Quality, 80L);
var codecs = ImageCodecInfo.GetImageEncoders();
var jpegCodec = codecs.FirstOrDefault(t => t.MimeType == "image/jpeg");
var encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = qualityParam;
foreach (var item in propertyItems)
{
b.SetPropertyItem(item);
}
b.Save(savePath, jpegCodec, encoderParams);
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
private static void CalculateNewRatio(int width, int height, int desiredSize, out int newWidth, out int newHeight)
{
if ((width >= height && width > desiredSize) || (width <= height && height > desiredSize))
{
if (width > height)
{
newWidth = desiredSize;
newHeight = height*newWidth/width;
}
else if (width < height)
{
newHeight = desiredSize;
newWidth = width*newHeight/height;
}
else
{
newWidth = desiredSize;
newHeight = desiredSize;
}
}
else
{
newWidth = width;
newHeight = height;
}
}
}
}
To scale an image onto a canvas for the best fit while preserving the aspect ratio, the process is fairly simple:
double widthFactor = b.Width / a.Width;
double heightFactor = b.Height / a.Height;
double scaleFactor = Math.Min(widthFactor, heightFactor);
int newWidth = a.Width * scaleFactor;
int newHeight = a.Width * scaleFactor;
To calculate the position to center the image just requires a little more math:
int left = (b.Width - newWidth) / 2;
int top = (b.Height - newHeight) / 2;
Then use the appropriate form of the Graphics.DrawImage method.
Related
I'm working on a Xamarin app where user's can pick Images using the Xamarin Essentials plugin. My issue now is finding away to shrink the image size using the image full path, before it's loaded to the cloud.
The Code
// Pick Image
private async Task PickImages()
{
if (ImageCollection.Count >= 10)
{
ToastMessageLong("Cannot Select More then 10 Images.");
return;
}
ImageLink image = new();
try
{
FileResult result = await MediaPicker.PickPhotoAsync(new MediaPickerOptions
{
Title = "Pick an Image"
});
if (result == null) return;
image.PostImages = result.FullPath;
ImageCollection.Add(image);
}
catch (Exception x)
{
await DisplayAlert("", x.Message);
}
}
private async Task UploadImagesToCloud()
{
if (ImageCollection.Count > 0)
{
List<ImageLink> imageLinks = new();
foreach (ImageLink img in ImageCollection)
{
// Need to Compress Image before adding to cloud..
ImageLink link = await CloudService.CS.UploadPostImage(img.PostImages);
imageLinks.Add(link);
}
P.Images = imageLinks;
}
}
You could resize the image size befor uploading it to cloud.Here is one method for resizing the image:
#if __IOS__
public static byte[] ResizeImageIOS(byte[] imageData, float width, float height)
{
UIImage originalImage = ImageFromByteArray(imageData);
UIImageOrientation orientation = originalImage.Orientation;
//create a 24bit RGB image
using (CGBitmapContext context = new CGBitmapContext(IntPtr.Zero,
(int)width, (int)height, 8,
4 * (int)width, CGColorSpace.CreateDeviceRGB(),
CGImageAlphaInfo.PremultipliedFirst))
{
RectangleF imageRect = new RectangleF(0, 0, width, height);
// draw the image
context.DrawImage(imageRect, originalImage.CGImage);
UIKit.UIImage resizedImage = UIKit.UIImage.FromImage(context.ToImage(), 0, orientation);
// save the image as a jpeg
return resizedImage.AsJPEG().ToArray();
}
}
#if __ANDROID__
public static byte[] ResizeImageAndroid (byte[] imageData, float width, float height)
{
// Load the bitmap
Bitmap originalImage = BitmapFactory.DecodeByteArray (imageData, 0, imageData.Length);
Bitmap resizedImage = Bitmap.CreateScaledBitmap(originalImage, (int)width, (int)height, false);
using (MemoryStream ms = new MemoryStream())
{
resizedImage.Compress (Bitmap.CompressFormat.Jpeg, 100, ms);
return ms.ToArray ();
}
}
you could refer to ImageResizer
Solution I was Given using SkiaSharp.
public static string CreateThumbnail(string Path, string fileName)
{
var bitmap = SKBitmap.Decode(Path);
int h = bitmap.Height;
int w = bitmap.Width;
int newWidth = w;
int newHeight = h;
//resize algorythm
if (h > 1080 || w > 1080)
{
int rectHeight = 1080;
int rectWidth = 1080;
//aspect ratio calculation
float W = w;
float H = h;
float aspect = W / H;
//new dimensions by aspect ratio
newWidth = (int)(rectWidth * aspect);
newHeight = (int)(newWidth / aspect);
//if one of the two dimensions exceed the box dimensions
if (newWidth > rectWidth || newHeight > rectHeight)
{
//depending on which of the two exceeds the box dimensions set it as the box dimension and calculate the other one based on the aspect ratio
if (newWidth > newHeight)
{
newWidth = rectWidth;
newHeight = (int)(newWidth / aspect);
}
else
{
newHeight = rectHeight;
newWidth = (int)(newHeight * aspect);
}
}
}
var resizedImage = bitmap.Resize(new SKImageInfo(newWidth, newHeight), SKBitmapResizeMethod.Lanczos3);
var image = resizedImage.Encode(SKEncodedImageFormat.Jpeg, 80);
var path = System.Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var filepath = System.IO.Path.Combine(path, fileName);
string finalPath = filepath;
using (var stream = File.OpenWrite(filepath))
image.SaveTo(stream);
return finalPath;
}
I try to resize a binary image to a smaller file if needed, however all images are getting more byte after resize, while size is getting smaller, so result is very ugly images...have no idea why its get bigger.
here is the code I use any help would be appreciated.
using (var srcImage = System.Drawing.Image.FromStream(myMemStream))
{
double height = srcImage.Height;
double width = srcImage.Width;
newWidth = (int)(srcImage.Width);
double aspect = scale / width;
newHeight = Convert.ToInt32(aspect * height);
newWidth = Convert.ToInt32(aspect * width);
using (var newImage = new Bitmap(newWidth, newHeight))
using (var graphics = Graphics.FromImage(newImage))
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawImage(srcImage, new Rectangle(0, 0, newWidth, newHeight));
System.IO.MemoryStream ms = new System.IO.MemoryStream();
newImage.Save(ms, format);
ms.Position = 0;
_bytes = ms.ToArray(); //Returns a new byte array.
newImage.Dispose();
}
}
**** update *****
double height = srcImage.Height;
double width = srcImage.Width;
newWidth = (int)(srcImage.Width);
double aspect = scale / width;
newHeight = Convert.ToInt32(aspect * height);
newWidth = Convert.ToInt32(aspect * width);
Image scaledImage = ScaleDownTo(srcImage, newHeight, newWidth);
newWidth = scaledImage.Width;
newHeight = scaledImage.Height;
System.IO.MemoryStream ms = new System.IO.MemoryStream();
scaledImage.Save(ms, format);
ms.Position = 0;
_bytes = ms.ToArray();
scaledImage.Dispose();
What I actually do is resize the image for example to a static max width, from any to 300px, so I calculate the current image width, take the aspect in double and resize this image to this size.
Any help on this is really appriciated
Well, here is a short function for scaling down images.
public static Image ScaleDownTo(Image image, int height, int width) {
if (image != null) {
if (image.Width > width || image.Height > height) {
float factor = Math.Max(((float)width) / image.Width, ((float)height) / image.Height);
if (factor > 0) {
RectangleF imgRect = new RectangleF(0, 0, image.Width * factor, image.Height * factor);
imgRect.X = ((float)width - imgRect.Width) / 2f;
imgRect.Y = ((float)height - imgRect.Height) / 2f;
Bitmap cellImage = new Bitmap(width, height);
using (Graphics cellImageGraphics = Graphics.FromImage(cellImage)) {
cellImageGraphics.Clear(Color.Transparent);
cellImageGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
cellImageGraphics.DrawImage(image, imgRect);
}
return cellImage;
}
}
return image;
}
return null;
}
Just use it to get the new downscaled image.
I am facing an interesting issue when re-sizing images of average dimension 1200x700. I am using following method to do the same. I see original image size is 210KB and output image size is 255KB.
public static Image ResizeImage(Image image, Size size, bool preserveAspectRatio = true)
{
int newWidth;
int newHeight;
if (preserveAspectRatio)
{
int originalWidth = imageFile.Width;
int originalHeight = imageFile.Height;
float percentWidth = (float)size.Width / (float)originalWidth;
float percentHeight = (float)size.Height / (float)originalHeight;
float percent = percentHeight < percentWidth ? percentHeight : percentWidth;
newWidth = (int)(originalWidth * percent);
newHeight = (int)(originalHeight * percent);
}
else
{
newWidth = size.Width;
newHeight = size.Height;
}
Image newImage = new Bitmap(newWidth, newHeight);
using (Graphics graphicsHandle = Graphics.FromImage(newImage))
{
graphicsHandle.CompositingQuality = CompositingQuality.HighQuality;
graphicsHandle.SmoothingMode = SmoothingMode.HighQuality;
graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphicsHandle.DrawImage(imageFile, 0, 0, newWidth, newHeight);
}
return newImage;
}
To call above method I'm using following code:
WebClient wc = new WebClient();
try
{
using (var image = Image.FromStream(new MemoryStream(wc.DownloadData(remoteFileLocation))))
{
Image resized = ResizeImage(image, new Size(660, 660));
resized.Save(saveFileLocation);
}
}
catch(Exception)
{
//todo
}
I tried different InterpolationMode and CompositingQuality but result is always same. Is there anything I'm missing to reduce image dimension and size? I mean, I'm expecting if dimension is decreased image size should also decrease.
I have to display image in photo gallery # width=200 height=180, but while uploading images I have to resize it , but the problem is every image have different resolution. How can I resize the images with different resolution so that images remain intact.
Here is my code :
private void ResizeImage()
{
System.Drawing.Image ImageToUpload = System.Drawing.Image.FromStream(FileUpload1.PostedFile.InputStream);
byte[] image = null;
int h = ImageToUpload.Height;
int w = ImageToUpload.Width;
int r = int.Parse(ImageToUpload.VerticalResolution.ToString());
int NewWidth = 200;//constant
int NewHeight = 180;//constant
byte[] imagesize = FileUpload1.FileBytes;
System.Drawing.Bitmap BitMapImage = new System.Drawing.Bitmap(ImageToUpload, NewWidth, NewHeight);//this line gives horrible output
MemoryStream Memory = new MemoryStream();
BitMapImage.Save(Memory, System.Drawing.Imaging.ImageFormat.Jpeg);
Memory.Position = 0;
image = new byte[Memory.Length + 1];
Memory.Read(image, 0, image.Length);
}
if resolution is 96 and if I set maxwidth=200 then its height would be 150 then only the image looks small and accurate. Can't we resize image in desired way so that it looks exact?
The function will resize the image maintaining aspect ratio.
public static Image Resize(Image originalImage, int w, int h)
{
//Original Image attributes
int originalWidth = originalImage.Width;
int originalHeight = originalImage.Height;
// Figure out the ratio
double ratioX = (double)w / (double)originalWidth;
double ratioY = (double)h / (double)originalHeight;
// use whichever multiplier is smaller
double ratio = ratioX < ratioY ? ratioX : ratioY;
// now we can get the new height and width
int newHeight = Convert.ToInt32(originalHeight * ratio);
int newWidth = Convert.ToInt32(originalWidth * ratio);
Image thumbnail = new Bitmap(newWidth, newHeight);
Graphics graphic = Graphics.FromImage(thumbnail);
graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphic.SmoothingMode = SmoothingMode.HighQuality;
graphic.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphic.CompositingQuality = CompositingQuality.HighQuality;
graphic.Clear(Color.Transparent);
graphic.DrawImage(originalImage, 0, 0, newWidth, newHeight);
return thumbnail;
}
Usage
Image BitMapImage = Resize(ImageToUpload, NewWidth, NewHeight);
Here i keep height fixed to 180 to maintain aspect ratio. It will resize the image and save to disk. The return value is the percentage value which i use in 'background-size' css.
public float ResizePhoto(string filepath, string filename)
{
var path = Path.Combine(filepath, filename);
var newPath = Path.Combine(filepath, "sml_" + filename);
Image orgImage = Image.FromFile(path);
float fixedHt = 180f;
int destHeight, destWidth;
float reqScale;
if(orgImage.Height > fixedHt)
{
destHeight = (int)fixedHt;
destWidth = (int)(fixedHt / orgImage.Height * orgImage.Width);
reqScale = destWidth / destHeight * 100;
}
else
{
destHeight = orgImage.Height;
destWidth = orgImage.Width;
reqScale = fixedHt / destHeight * 100;
}
Bitmap bmp = new Bitmap(destWidth, destHeight);
bmp.SetResolution(orgImage.HorizontalResolution,orgImage.VerticalResolution);
Graphics grPhoto = Graphics.FromImage(bmp);
grPhoto.DrawImage(orgImage,
new Rectangle(0, 0, destWidth, destHeight),
new Rectangle(0, 0, orgImage.Width, orgImage.Height),
GraphicsUnit.Pixel);
bmp.Save(newPath);
return reqScale;
}
I have created Tiff file viewer for my web application and it's working great. But the problem is there are some big images (4000 X 2000 - 5+ MB) which I need to display in it's original size.
Is it possible to compress the size of the image and then send the stream to Response Stream on the ashx page?
Note that all my images are scanned black and white images. However for precaution I would like to check whether compression algorithm specific to black & white image be applied to the image or not.
Look the below code of TiffHelper class
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Drawing.Drawing2D;
public class TiffHelper
{
public static int GetPageCount(String filePath)
{
int pageCount = 0;
try
{
using (Image img = Image.FromStream(
new FileStream(filePath,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite)
))
{
pageCount = img.GetFrameCount(FrameDimension.Page);
}
}
catch (Exception ex)
{
CException.SuppressException(ex);
pageCount = -1;
}
return pageCount;
}
public static Image GetTiffImage(String sourceFile, int pageNumber, int thumbnailSize, int degree)
{
MemoryStream ms = null;
Image sourceImage = null;
Image returnImage = null;
try
{
sourceImage = Image.FromStream(
new FileStream(sourceFile,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite)
);
ms = new MemoryStream();
FrameDimension FrDim = new FrameDimension(sourceImage.FrameDimensionsList[0]);
sourceImage.SelectActiveFrame(FrDim, pageNumber - 1);
sourceImage.Save(ms, ImageFormat.Tiff);
/*
EncoderParameter qualityParam = new EncoderParameter(Encoder.Quality, 90);
ImageCodecInfo tiffCodec = GetEncoderInfo("image/tiff");
EncoderParameters encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = qualityParam;
sourceImage.Save(ms, tiffCodec, encoderParams);
*/
/*
EncoderParameters iparams = new EncoderParameters(2);
iparams.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.SaveFlag, (long)EncoderValue.MultiFrame);
iparams.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.Compression, (long)EncoderValue.CompressionCCITT4);
*
sourceImage.Save(ms, GetEncoderInfo("image/tiff"), iparams);
*/
int height = sourceImage.Height;
int width = sourceImage.Width;
if (thumbnailSize > 0)
{
if (sourceImage.Width < sourceImage.Height)
{
width = thumbnailSize;
height = sourceImage.Height * thumbnailSize / sourceImage.Width;
}
else
{
width = sourceImage.Width * thumbnailSize / sourceImage.Height;
height = thumbnailSize;
}
}
//returnImage = ResizeImage(Image.FromStream(ms), new Size(width, height));
returnImage = Image.FromStream(ms).GetThumbnailImage(width, height, null, IntPtr.Zero);
returnImage.RotateFlip(GetFlipType(degree));
}
catch (Exception ex)
{
CException.SuppressException(ex);
}
finally
{
ms.Dispose();
sourceImage.Dispose();
}
return returnImage;
}
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.SmoothingMode = SmoothingMode.HighSpeed;
g.PixelOffsetMode = PixelOffsetMode.HighSpeed;
g.CompositingQuality = CompositingQuality.HighQuality;
g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
g.Dispose();
return (Image)b;
}
private static ImageCodecInfo GetEncoderInfo(string mimeType)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
for (int i = 0; i < codecs.Length; i++)
{
if (codecs[i].MimeType == mimeType)
{
return codecs[i];
}
}
return null;
}
private static RotateFlipType GetFlipType(int degree)
{
switch (degree)
{
case 90:
return RotateFlipType.Rotate90FlipNone;
case 180:
return RotateFlipType.Rotate180FlipNone;
case 270:
return RotateFlipType.Rotate270FlipNone;
case -90:
return RotateFlipType.Rotate90FlipXY;
case -180:
return RotateFlipType.Rotate180FlipNone;
case -270:
return RotateFlipType.Rotate270FlipXY;
default:
return RotateFlipType.RotateNoneFlipNone;
}
}
}