The image gets distorted while cropping it into various sizes. How do I do this without affecting the image quality? My current result is a distorted blurry image. Please help.
Here is my code:
var common = new Common();
string filesPath = HttpContext.Current.Server.MapPath(Const.directoryPath);
string imageUrl1 = UploadImageToAzure(1123, "\\Configurator\\_trunk\\Content\\TempImages\\eddaec5aa33e4b1593b304674a842874.jpeg, "eddaec5aa33e4b1593b304674a842874_260x190.jpeg", cloudstorage, containerName);
string cropimage260x190 = CropImagewithName(inputStream/*System.Io.Stream*/, 260, 190, cropedImageName);
public string CropImagewithName(Stream stream, int width, int height, string name)
{
int bmpW = 0;
int bmpH = 0;
string filePath = string.Empty;
string imgName = string.Empty;
try
{
{
bmpW = width;
bmpH = height;
int newWidth = bmpW;
int newHeight = bmpH;
imgName = name;
imgName = imgName.Replace("-", "");
filePath = Const.directoryPath + imgName;
this.upBmp = new Bitmap(stream);
this.newBmp = new Bitmap(newWidth, newHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
this.newBmp.SetResolution(300, 300);
int upWidth = this.upBmp.Width;
int upHeight = this.upBmp.Height;
int newX = 0;
int newY = 0;
decimal reDuce;
if (upWidth > upHeight)
{
reDuce = Convert.ToDecimal(newWidth) / Convert.ToDecimal(upWidth);
newHeight = Convert.ToInt32((Convert.ToDecimal(upHeight) * reDuce));
newY = (bmpH - newHeight) / 2;
newX = 0;
}
else if (upWidth < upHeight)
{
reDuce = Convert.ToDecimal(newHeight) / Convert.ToDecimal(upHeight);
newWidth = Convert.ToInt32((Convert.ToDecimal(upWidth) * reDuce));
newX = (bmpW - newWidth) / 2;
newY = 0;
}
else if (upWidth == upHeight) //
{
reDuce = Convert.ToDecimal(newHeight) / Convert.ToDecimal(upHeight);
newWidth = Convert.ToInt32((Convert.ToDecimal(upWidth) * reDuce));
newX = (bmpW - newWidth) / 2;
newY = (bmpH - newHeight) / 2;
}
newGraphic = Graphics.FromImage(newBmp);
this.newGraphic.Clear(Color.White);
this.newGraphic.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
this.newGraphic.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
newGraphic.DrawImage(upBmp, newX, newY, newWidth, newHeight);
newBmp.Save(HttpContext.Current.Server.MapPath(filePath), System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
catch (Exception)
{
}
finally
{
this.upBmp.Dispose();
this.newBmp.Dispose();
this.newGraphic.Dispose();
}
return imgName;
}
You are experiencing JPEG compression artifacts, not geometrical distortion. You need to set JPEG compression quality before saving your image. Here is how you can save your image with highest quality (look for 100L in the code below):
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == ImageFormat.Jpeg.Guid)
{
var myEncoder = System.Drawing.Imaging.Encoder.Quality;
var myEncoderParameter = new EncoderParameter(myEncoder, 100L);
var myEncoderParameters = new EncoderParameters(1) { Param = { [0] = myEncoderParameter } };
newBmp.Save(#"C:\qqq\111.jpeg", codec, myEncoderParameters);
break;
}
}
Here is the MSDN article for it: https://msdn.microsoft.com/en-us/library/bb882583(v=vs.110).aspx
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 have written the following code to upload different types of images.
public static void CompressImageWithNewDimensions(Stream stream, string targetPath, double fileSizeKB)
{
try
{
using (var image = Image.FromStream(stream))
{
double scaleFactor;
if (fileSizeKB <= 900)
{
scaleFactor = 0.9;
}
else if (fileSizeKB <= 1500)
{
scaleFactor = 0.8;
}
else if (fileSizeKB <= 2000)
{
scaleFactor = 0.7;
}
else
{
scaleFactor = 0.3;
}
var newWidth = (int)(image.Width * scaleFactor);
var newHeight = (int)(image.Height * scaleFactor);
var CompressImage = new Bitmap(newWidth, newHeight);
var CompressImageGraph = Graphics.FromImage(CompressImage);
CompressImageGraph.CompositingQuality = CompositingQuality.HighQuality;
CompressImageGraph.SmoothingMode = SmoothingMode.HighQuality;
CompressImageGraph.InterpolationMode = InterpolationMode.HighQualityBicubic;
var imageRectangle = new Rectangle(0, 0, newWidth, newHeight);
CompressImageGraph.DrawImage(image, imageRectangle);
//CompressImage.Save(targetPath, image.RawFormat);
CompressImage.Save(targetPath, System.Drawing.Imaging.ImageFormat.Gif);
}
}
catch (Exception)
{
}
}
Now I have successfully uploaded GIF file in my folder, but in the folder when I am opening the image, it is not animating.
I was using Graphics.DrawImage(DrawText()) to DrawText into Image.
The problem is: I only draw three text but origin images size is: 226kb and output image when Save() ~3.45mb. It too larger.
Image dimensions: 2732 * 3200.
I only loop my list textFileSplit, and this list only has three items.
This is all my code to save image:
foreach (string text in lstTextFromFile)
{
count++;
if (text == "") continue;
Graphics gra = Graphics.FromImage(img);
string st = lstImgAdded.Items[k].Text;
Bitmap bmp = new Bitmap(#"" + st);
bmp = (Bitmap)ResizePanel(bmp, panel2);
panel2.BackgroundImage = bmp;
Graphics gbmp = Graphics.FromImage(bmp);
string[] textFileSplit = text.Split('-');
for (int u = 0; u < textFileSplit.Count(); u++)
{
myColorLabel = activeLabels[u+1].ForeColor;
gbmp.DrawImage(
DrawText(textFileSplit[u], fontType, myColorLabel,
Color.Transparent),
Point.Round(StretchImageSize(new Point(activeLabels[u+1].Location.X, activeLabels[u+1].Location.Y), panel2)));
}
gra.Dispose();
Guid id = Guid.NewGuid();
ScaleImage(bmp, witdhImg, heightImg)
.Save(linkLocation + "\\" + id + "." + imgType,
ImageFormat.Png);
}
In class ScaleImage() I tried to keep dimensions like origin images:
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 ratio2 = Math.Max(ratioX, ratioY);
var newWidth = (int)(image.Width * ratio);
var newHeight = (int)(image.Height * ratio2);
var newImage = new Bitmap(newWidth, newHeight);
Graphics.FromImage(newImage).DrawImage(image, 0, 0, newWidth, newHeight);
return newImage;
}
Make sure to set the resolution and quality in your scaling function:
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 ratio2 = Math.Max(ratioX, ratioY);
var newWidth = (int)(image.Width * ratio);
var newHeight = (int)(image.Height * ratio2);
var newImage = var newImage = new Bitmap(newWidth, newHeight, image.PixelFormat);
newImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
Graphics grPhoto = Graphics.FromImage(newImage);
grPhoto.InterpolationMode = InterpolationMode.High;
grPhoto.DrawImage(image, 0, 0, newWidth, newHeight);
grPhoto.Dispose();
return newImage;
}
Apply EncoderParameters with low Quality:
ImageCodecInfo pngEncoder = ImageCodecInfo.GetImageDecoders().Where(k=> k.FormatID == ImageFormat.Png.Guid).First();
EncoderParameters encoderParameters;
encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, 60L);
ScaleImage(bmp, witdhImg, heightImg)
.Save(linkLocation + "\\" + id + "." + imgType, pngEncoder ,encoderParameters);
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;
}
}
}
I have an image in an array and I'm saving it this way:
ImageConverter ic = new ImageConverter();
Image img = (Image)ic.ConvertFrom(Jpeg);
Bitmap bitmap1 = new Bitmap(img);
string saveString = "c:\\M_files\\new_pics\\" + pictureCounter + ".jpg";
bitmap1.Save(saveString, System.Drawing.Imaging.ImageFormat.Jpeg);
It works but I need it to be faster since its an image from camera that needs to stream. Is there a faster way? My array is in bytes then I use the container to convert to bitmap then save as jpeg.
try this code, this works fast and efficient, in this piece of code you can convert a picture to JPEG specitfying the width and height, and can pass as many number of images as you want. You can modify it to your own Requirement.
public void CreateThumbnail(string[] b, double wid, double hght, bool Isprint)
{
string[] path;
path = new string [64];
path = b;
string saveath = "i:\\check\\a test\\";
for (int i = 0; i < b.Length; i++)
{
DirectoryInfo dir = new DirectoryInfo(path[i]);
string dir1 = dir.ToString();
dir1 = dir1.Substring(dir1.LastIndexOf("\\"));
FileInfo[] files1 = dir.GetFiles();
foreach (FileInfo f in files1)
{
string gh = f.ToString();
try
{
System.Drawing.Image myThumbnail150;
System.Drawing.Image.GetThumbnailImageAbort myCallback = new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback);
System.Drawing.Image imagesize = System.Drawing.Image.FromFile(f.FullName);
Bitmap bitmapNew = new Bitmap(imagesize);
double maxWidth = wid;
double maxHeight = hght;
int w = imagesize.Width;
int h = imagesize.Height;
// Longest and shortest dimension
int longestDimension = (w > h) ? w : h;
int shortestDimension = (w < h) ? w : h;
// propotionality
float factor = ((float)longestDimension) / shortestDimension;
// default width is greater than height
double newWidth = maxWidth;
double newHeight = maxWidth / factor;
// if height greater than width recalculate
if (w < h)
{
newWidth = maxHeight / factor;
newHeight = maxHeight;
}
myThumbnail150 = bitmapNew.GetThumbnailImage((int)newWidth, (int)newHeight, myCallback, IntPtr.Zero);
string ext = Path.GetExtension(f.Name);
if (!Directory.Exists(saveath + dir1))
{
Directory.CreateDirectory(saveath + dir1);
myThumbnail150.Save(saveath + dir1 + "\\" + f.Name.Replace(ext, ".Jpeg"), System.Drawing.Imaging.ImageFormat.Jpeg);
}
else if(Directory.Exists(saveath+dir1))
{
myThumbnail150.Save(saveath + dir1+" \\"+ f.Name.Replace(ext, ".Jpeg"), System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
catch (Exception ex)
{
Console.WriteLine("something went wrong" + ex.ToString());
}
}
}
}