I am working on xamarin.forms. I have to select images from gallery and then resize them and then upload them on server. But I don't know how I can resize selected image in a given particular size?
Please update me how I can do this?
This can be used with a stream (if you're using the Media Plugin https://github.com/jamesmontemagno/MediaPlugin) or standard byte arrays.
// If you already have the byte[]
byte[] resizedImage = await CrossImageResizer.Current.ResizeImageWithAspectRatioAsync(originalImageBytes, 500, 1000);
// If you have a stream, such as:
// var file = await CrossMedia.Current.PickPhotoAsync(options);
// var originalImageStream = file.GetStream();
byte[] resizedImage = await CrossImageResizer.Current.ResizeImageWithAspectRatioAsync(originalImageStream, 500, 1000);
I tried use CrossImageResizer.Current... but I did not find it in the Media Plugin. Instead I found an option called MaxWidthHeight, that worked only if you also add PhotoSize = PhotoSize.MaxWidthHeight option.
For Example :
var file = await CrossMedia.Current.PickPhotoAsync(new PickMediaOptions() { PhotoSize = PhotoSize.MaxWidthHeight, MaxWidthHeight = 600 });
var file = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions { PhotoSize = PhotoSize.MaxWidthHeight, MaxWidthHeight = 600 });
Sadly enough there isn't a good cross-platform image resizer (that I've found at the time of this post). Image processing wasn't really designed to take place in a cross-platform environment for iOS and Android. It's much faster and cleaner to perform this on each platform using platform-specific code. You can do this using dependency injection and the DependencyService (or any other service or IOC).
AdamP gives a great response on how to do this Platform Specific Image Resizing
Here is the code taken from the link above.
iOS
public class MediaService : IMediaService
{
public byte[] ResizeImage(byte[] imageData, float width, float height)
{
UIImage originalImage = ImageFromByteArray(imageData);
var originalHeight = originalImage.Size.Height;
var originalWidth = originalImage.Size.Width;
nfloat newHeight = 0;
nfloat newWidth = 0;
if (originalHeight > originalWidth)
{
newHeight = height;
nfloat ratio = originalHeight / height;
newWidth = originalWidth / ratio;
}
else
{
newWidth = width;
nfloat ratio = originalWidth / width;
newHeight = originalHeight / ratio;
}
width = (float)newWidth;
height = (float)newHeight;
UIGraphics.BeginImageContext(new SizeF(width, height));
originalImage.Draw(new RectangleF(0, 0, width, height));
var resizedImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
var bytesImagen = resizedImage.AsJPEG().ToArray();
resizedImage.Dispose();
return bytesImagen;
}
}
Android
public class MediaService : IMediaService
{
public byte[] ResizeImage(byte[] imageData, float width, float height)
{
// Load the bitmap
BitmapFactory.Options options = new BitmapFactory.Options();// Create object of bitmapfactory's option method for further option use
options.InPurgeable = true; // inPurgeable is used to free up memory while required
Bitmap originalImage = BitmapFactory.DecodeByteArray(imageData, 0, imageData.Length, options);
float newHeight = 0;
float newWidth = 0;
var originalHeight = originalImage.Height;
var originalWidth = originalImage.Width;
if (originalHeight > originalWidth)
{
newHeight = height;
float ratio = originalHeight / height;
newWidth = originalWidth / ratio;
}
else
{
newWidth = width;
float ratio = originalWidth / width;
newHeight = originalHeight / ratio;
}
Bitmap resizedImage = Bitmap.CreateScaledBitmap(originalImage, (int)newWidth, (int)newHeight, true);
originalImage.Recycle();
using (MemoryStream ms = new MemoryStream())
{
resizedImage.Compress(Bitmap.CompressFormat.Png, 100, ms);
resizedImage.Recycle();
return ms.ToArray();
}
}
WinPhone
public class MediaService : IMediaService
{
private MediaImplementation mi = new MediaImplementation();
public byte[] ResizeImage(byte[] imageData, float width, float height)
{
byte[] resizedData;
using (MemoryStream streamIn = new MemoryStream(imageData))
{
WriteableBitmap bitmap = PictureDecoder.DecodeJpeg(streamIn, (int)width, (int)height);
float Height = 0;
float Width = 0;
float originalHeight = bitmap.PixelHeight;
float originalWidth = bitmap.PixelWidth;
if (originalHeight > originalWidth)
{
Height = height;
float ratio = originalHeight / height;
Width = originalWidth / ratio;
}
else
{
Width = width;
float ratio = originalWidth / width;
Height = originalHeight / ratio;
}
using (MemoryStream streamOut = new MemoryStream())
{
bitmap.SaveJpeg(streamOut, (int)Width, (int)Height, 0, 100);
resizedData = streamOut.ToArray();
}
}
return resizedData;
}
}
EDIT: If you are already using FFImageLoading in your project then you can just use that for your platform.
https://github.com/luberda-molinet/FFImageLoading
I fixed in my project, this was the best way for me .
when take photo or get image from gallery you can change size with properties
var file = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions
{
PhotoSize = PhotoSize.Custom,
CustomPhotoSize = 90 //Resize to 90% of original
});
for more information: https://github.com/jamesmontemagno/MediaPlugin
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 need to resize my image proportionately without changing aspect ratio.I have the code to resize with fixed hight and width but I need to resize image proportionately with max height(say 600 pixels). How can I modify the code to suit my requirement?
public static void Main()
{
var image = Image.FromFile(#"c:\logo.png");
var newImage = ScaleImage(image, 300, 400);
newImage.Save(#"c:\test.png", ImageFormat.Png);
}
public static 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(newWidth, newHeight);
Graphics.FromImage(newImage).DrawImage(image, 0, 0, newWidth, newHeight);
return newImage;
}
Please provide your valuable thoughts.
This almost feels to easy and I feel I'm missing something. Anyway, won't that do the trick?
public static Image ScaleImage(Image image, int maxHeight)
{
var ratio = (double)maxHeight / image.Height;
var newWidth = (int)(image.Width * ratio);
var newHeight = (int)(image.Height * ratio);
var newImage = new Bitmap(newWidth, newHeight);
using (var g = Graphics.FromImage(newImage))
{
g.DrawImage(image, 0, 0, newWidth, newHeight);
}
return newImage;
}
Use the following function
public Bitmap ProportionallyResizeBitmapByHeight(Bitmap imgToResize, int height)
{
int sourceWidth = imgToResize.Width;
int sourceHeight = imgToResize.Height;
float scale = 0;
scale = (height / (float)sourceHeight);
int destWidth = (int)(sourceWidth * scale);
int destHeight = (int)(sourceHeight * scale);
Bitmap result = new Bitmap(destWidth, destHeight);
result.SetResolution(imgToResize.HorizontalResolution, imgToResize.VerticalResolution);
Graphics g = Graphics.FromImage(result);
g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
g.Dispose();
return result;
}
Well, thinking through the process:
if you have an image that 800 x 600 in size and want to resize it to newWidth x 400 height (plus whatever the respective newWidth will be), you get the ratio by dividing the newHeight (maxHeight in your case) with 600 and multiply 800 with this ratio, right?
So, in this case you need to change maxWidth and maxHeight to optional parameters (say 800 by 600) to give yourself some dynamism and get the following:
public static Image ScaleImage(Image image, int maxWidth = 800, int maxHeight = 600)
{
int newWidth;
int newHeight;
double ratio = image.Height / image.Width;
if(maxHeight != 600) {
newWidth = image.Width * ratio;
newHeight = maxHeight;
}
Bitmap newImage = new Bitmap(newWidth, newHeight);
Graphics.FromImage(newImage).DrawImage(image, 0, 0, newWidth, newHeight);
return newImage;
}
I hope this helps. I didn't test it, but I've rewritten my VB code, so allegedly it should be okay...
There's also a ResizeStream method here: http://forums.asp.net/t/1576697.aspx/1 that you might find useful.
If you want to keep image quality, you can use the CompositingQuality and SmoothingMode, etc. variables.
100% Worked
private static BitmapFrame CreateResizedImage(ImageSource source, int Max_width, int Max_height, int margin)
{
float scaleHeight = (float)Max_width / (float)source.Height;
float scaleWidth = (float)Max_height / (float)source.Width;
float scale = Math.Min(scaleHeight, scaleWidth);
int width = (int)(source.Width * scale);
int height = (int)(source.Height * scale);
var rect = new Rect(margin, margin, width - margin * 2, height - margin * 2);
var group = new DrawingGroup();
RenderOptions.SetBitmapScalingMode(group, BitmapScalingMode.HighQuality);
group.Children.Add(new ImageDrawing(source, rect));
var drawingVisual = new DrawingVisual();
using (var drawingContext = drawingVisual.RenderOpen())
drawingContext.DrawDrawing(group);
var resizedImage = new RenderTargetBitmap(
width, height, // Resized dimensions
96, 96, // Default DPI values
PixelFormats.Default); // Default pixel format
resizedImage.Render(drawingVisual);
return BitmapFrame.Create(resizedImage);
}
//--------Main------------//
BitmapImage imageSource = (BitmapImage)ImagePreview.Source;
var NewImage= CreateResizedImage(imageSource , 300, 300, 0);