Loading code from filesystem:
System.Drawing.Image image = System.Drawing.Image.FromFile(<location of original image>););
Loading code from browser request:
var memoryStream = new MemoryStream();
using (memoryStream)
{
System.Web.HttpContext.Current.Request.Files[upload].InputStream.CopyTo(memoryStream);
memoryStream.ToArray();
}
byte[] bytes = memoryStream.GetBuffer();
// Get the image from the server
System.Drawing.Image image = new System.Drawing.Bitmap( System.Web.HttpContext.Current.Request.Files[upload].InputStream );
Resize image call:
System.Drawing.Image image = this.ResizeImage(
image,
originalImagePath,
ImageSizeType.Original,
null,
null)
Save image call:
image.Save(<location to save>);
The code that doesn't compress the image:
private System.Drawing.Image ResizeImage(System.Drawing.Image image, string filePath, string sizeType, int? _width, int? height )
{
...
System.Drawing.Bitmap b = new System.Drawing.Bitmap(width, resizeHeight);
b.SetResolution(72, 72);
System.Drawing.Graphics g = System.Drawing.Graphics.FromImage((System.Drawing.Image)b);
g.CompositingQuality = CompositingQuality.HighSpeed;
//g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.InterpolationMode = InterpolationMode.Low;
g.SmoothingMode = SmoothingMode.HighSpeed;
g.DrawImage(image, 0, 0, width, resizeHeight);
g.Dispose();
return (System.Drawing.Image)b;
}
No matter what I do to this image, when it saves, it saves at a really high kb.
For example... a jpg of 1024 x 768 # 300kb becomes 600 x 400 # 800kb
What am I doing wrong?
As Magnus rightly said, the drawing to the canvas makes no difference to size... of file...
It was the save file part that was being all noob... This is what it should be:
private ImageCodecInfo GetEncoderInfo(string mimeType)
{
// Get image codecs for all image formats
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
// Find the correct image codec
for (int i = 0; i < codecs.Length; i++)
if (codecs[i].MimeType == mimeType)
return codecs[i];
return null;
}
...
if( mimeType.ToLower() == "image/jpeg")
{
ImageCodecInfo jpgEncoder = this.GetEncoderInfo("image/jpeg")
System.Drawing.Imaging.Encoder myEncoder = System.Drawing.Imaging.Encoder.Quality;
EncoderParameters myEncoderParameters = new EncoderParameters(1);
EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, 80L);
myEncoderParameters.Param[0] = myEncoderParameter;
image.Save(systemFilePath, jpgEncoder, myEncoderParameters);
}
else
{
image.Save(systemFilePath);
}
Related
I am trying out working with images and i came up to some strange behavior when using Encoder.Quality
I am having code like this:
try
{
Image myImage = Image.FromStream(file.OpenReadStream());
string final = "";
MemoryStream ts = new MemoryStream();
SaveJpeg(ts, myImage, 100);
final += (ts.Capacity * 0.0009765625).ToString("0") + ", ";
for (int i = 0; i <= 10; i++)
{
MemoryStream testStream = new MemoryStream();
SaveJpeg(testStream, myImage, i * 10);
final += (testStream.Capacity * 0.0009765625).ToString("0") + ", ";
}
return Json(final);
}
catch (Exception ex)
{
return View("Error", ex.ToString());
}
public static void SaveJpeg(string path, Image img, int quality)
{
if (quality < 0)
quality = 0;
if (quality > 100)
quality = 100;
// Encoder parameter for image quality
EncoderParameter qualityParam = new EncoderParameter(Encoder.Quality, quality);
// JPEG image codec
ImageCodecInfo jpegCodec = GetEncoderInfo("image/jpeg");
EncoderParameters encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = qualityParam;
img.Save(path, jpegCodec, encoderParams);
}
public static void SaveJpeg(Stream stream, Image img, int quality)
{
if (quality < 0)
quality = 0;
if (quality > 100)
quality = 100;
// Encoder parameter for image quality
EncoderParameter qualityParam = new EncoderParameter(Encoder.Quality, quality);
// JPEG image codec
ImageCodecInfo jpegCodec = GetEncoderInfo("image/jpeg");
EncoderParameters encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = qualityParam;
img.Save(stream, jpegCodec, encoderParams);
}
/// <summary>
/// Returns the image codec with the given mime type
/// </summary>
private static ImageCodecInfo GetEncoderInfo(string mimeType)
{
// Get image codecs for all image formats
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
// Find the correct image codec
for (int i = 0; i < codecs.Length; i++)
if (codecs[i].MimeType == mimeType)
return codecs[i];
return null;
}
It is asp.net core but i think that dosn't matter.
What matters here is that file i upload is "jpg" and it's size is 201kb but output from this code is 512, 29, 41, 53, 128, 128, 128, 128, 128, 128, 256, 512, (first number is quality 100, others are 0, 10, 20... 100)
When i store it into MemoryStream without changing quality i get same file size as my file.
Here i have two questions.
Is quality level 90-100 somehow "enchancing" image quality?
Why is image size from level 40-90 all same size, does it make any difference?
I've seen a ton of stackoverflow articles for reducing image size, but none of them maintain the original image type (or so I've found). They usually have steps to reduce pixel dimensions, reduce image quality, and convert to a specific type of image (usually jpeg).
I have a group of images that I need to resize. They have various image types, and the filenames are all stored in a database, which makes converting from one image type to another somewhat problematic. I can't just change the filename from png to jpg because then the database won't point at a real file.
Doe anyone have an example of how to resize / reduce images to '256 kilobytes' and maintain the original image type?
For examples, here is the code I'm currently fiddling with.
public static byte[] ResizeImageFile(Image oldImage, int targetSize) // Set targetSize to 1024
{
Size newSize = CalculateDimensions(oldImage.Size, targetSize);
using (Bitmap newImage = new Bitmap(newSize.Width, newSize.Height, PixelFormat.Format24bppRgb))
{
using (Graphics canvas = Graphics.FromImage(newImage))
{
canvas.SmoothingMode = SmoothingMode.AntiAlias;
canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
canvas.PixelOffsetMode = PixelOffsetMode.HighQuality;
canvas.DrawImage(oldImage, new Rectangle(new Point(0, 0), newSize));
MemoryStream m = new MemoryStream();
newImage.Save(m, ImageFormat.Jpeg);
return m.GetBuffer();
}
}
}
Maybe there is a way I can get file fileinfo or mime type first and then switch on the .Save for the type of image?
Here is what I came up with (based on some examples that I found online that weren't 100% complete.
private void EnsureImageRequirements(string filePath)
{
try
{
if (File.Exists(filePath))
{
// If images are larger than 300 kilobytes
FileInfo fInfo = new FileInfo(filePath);
if (fInfo.Length > 300000)
{
Image oldImage = Image.FromFile(filePath);
ImageFormat originalFormat = oldImage.RawFormat;
// manipulate the image / Resize
Image tempImage = RefactorImage(oldImage, 1200); ;
// Dispose before deleting the file
oldImage.Dispose();
// Delete the existing file and copy the image to it
File.Delete(filePath);
// Ensure encoding quality is set to an acceptable level
ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
// Set encoder to fifty percent compression
EncoderParameters eps = new EncoderParameters
{
Param = { [0] = new EncoderParameter(Encoder.Quality, 50L) }
};
ImageCodecInfo ici = (from codec in encoders where codec.FormatID == originalFormat.Guid select codec).FirstOrDefault();
// Save the reformatted image and use original file format (jpeg / png / etc) and encoding
tempImage.Save(filePath, ici, eps);
// Clean up RAM
tempImage.Dispose();
}
}
}
catch (Exception ex)
{
this._logger.Error("Could not resize oversized image " + filePath, ex);
}
}
private static Image RefactorImage(Image imgToResize, int maxPixels)
{
int sourceWidth = imgToResize.Width;
int sourceHeight = imgToResize.Height;
int destWidth = sourceWidth;
int destHeight = sourceHeight;
// Resize if needed
if (sourceWidth > maxPixels || sourceHeight > maxPixels)
{
float thePercent = 0;
float thePercentW = 0;
float thePercentH = 0;
thePercentW = maxPixels / (float) sourceWidth;
thePercentH = maxPixels / (float) sourceHeight;
if (thePercentH < thePercentW)
{
thePercent = thePercentH;
}
else
{
thePercent = thePercentW;
}
destWidth = (int)(sourceWidth * thePercent);
destHeight = (int)(sourceHeight * thePercent);
}
Bitmap tmpImage = new Bitmap(destWidth, destHeight, PixelFormat.Format24bppRgb);
Graphics g = Graphics.FromImage(tmpImage);
g.InterpolationMode = InterpolationMode.HighQualityBilinear;
g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
g.Dispose();
return tmpImage;
}
I am trying to export pdfs as jpeg using pdfium viewer. How can I specify the color depth of the jpeg image? Do I have to edit the stream using System.Drawing.Imaging or can does anyone know of a way to do it with pdfium? https://github.com/pvginkel/PdfiumViewer is what I am using to render the pdf and create the image.
using (var document = PdfiumViewer.PdfDocument.Load(file.ToString()))
{
for (int page = 0; page < document.PageCount; page++)
{
var image = document.Render(page, 2550, 3300, 72, 72, false);
image.Save(filepath + "\\" + filename + "-" + (page + 1) + ".jpg", ImageFormat.Jpeg);
}
}
Have to set the image encoder properties to change them. This is showing from bump to tiff but explains what I was looking for.
using System;
using System.Drawing;
using System.Drawing.Imaging;
class Example_SetColorDepth
{
public static void Main()
{
Bitmap myBitmap;
ImageCodecInfo myImageCodecInfo;
Encoder myEncoder;
EncoderParameter myEncoderParameter;
EncoderParameters myEncoderParameters;
// Create a Bitmap object based on a BMP file.
myBitmap = new Bitmap(#"C:\Documents and Settings\All Users\Documents\My Music\music.bmp");
// Get an ImageCodecInfo object that represents the TIFF codec.
myImageCodecInfo = GetEncoderInfo("image/tiff");
// Create an Encoder object based on the GUID
// for the ColorDepth parameter category.
myEncoder = Encoder.ColorDepth;
// Create an EncoderParameters object.
// An EncoderParameters object has an array of EncoderParameter
// objects. In this case, there is only one
// EncoderParameter object in the array.
myEncoderParameters = new EncoderParameters(1);
// Save the image with a color depth of 24 bits per pixel.
myEncoderParameter =
new EncoderParameter(myEncoder, 24L);
myEncoderParameters.Param[0] = myEncoderParameter;
myBitmap.Save("Shapes24bpp.tiff", myImageCodecInfo, myEncoderParameters);
}
private static ImageCodecInfo GetEncoderInfo(String mimeType)
{
int j;
ImageCodecInfo[] encoders;
encoders = ImageCodecInfo.GetImageEncoders();
for(j = 0; j < encoders.Length; ++j)
{
if(encoders[j].MimeType == mimeType)
return encoders[j];
}
return null;
}
}
I want to re-size an image to specifically 150*150 dimension and the image size should be less than or equal to 10kb. I already done with Bitmap, the code snippet is given below :
System.Drawing.Image old_Image=System.Drawing.Image.FromFile(//ImagePath);
Bitmap new_Image= new Bitmap(150, 150, PixelFormat.Format24bppRgb);
Graphics g=new Graphics();
g= Graphics.FromImage(new_Image);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
g.DrawImage(old_Image, new Rectangle(new Point(0, 0), new_Image));
When I did like this, I got all the images with dimension 150*150 and the size is less than 10kb. But How can I ensure that the image size will be less than 10kb every time for every image of greater original size. Can I make sure it by checking it in the code side? Or is there any alternate method for this?
UPDATE:
I have compressed the image and need to do the same as long as the image size becomes less than 10kb. The loop working fine for first Iteration and for the second time, I am getting error 'Parameter not valid' when calling b.Save(ms, ici, ep). I already call dispose for Bitmap and MemoryStream after first Iteration. Then what could be the reason for this error.
The code snippet, I am using for the iteration is given below:
while (length > 10240)
{
long quality = 50;
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
ImageCodecInfo ici = null;
foreach (ImageCodecInfo codec in codecs)
{
if (codec.MimeType == "image/jpeg")
ici = codec;
}
EncoderParameters ep = new EncoderParameters();
ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
ms = new MemoryStream();
b.Save(ms, ici, ep);
using (Bitmap bp = new Bitmap(ms))
{
b = bp;
}
length = Convert.ToInt32(ms.Length);
ms.Dispose();
}
You could get the output size of your image like this:
using (var testStream = new MemoryStream())
{
b.Save(testStream, ImageFormat.Jpeg);
long streamSize = testStream.Length;
}
b is your image.
If you want to compress it even more to reduce the size of the image, try this:
//quality of the compressed image
long quality = 50;
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
ImageCodecInfo ici = null;
foreach (ImageCodecInfo codec in codecs)
{
if (codec.MimeType == "image/jpeg")
ici = codec;
}
EncoderParameters ep = new EncoderParameters();
ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
using (var testStream = new MemoryStream())
{
b.Save(testStream, ici, ep);
long streamSize = testStream.Length;
}
We're doing the same as above, but we now have the possibility to reduce the quality of the jpeg compression.
i get an
A generic error occurred in GDI+
exception when I call img.Save(path, jpegCodec, encoderParams);
here is all of the code :
private Image img;
private void button1_Click(object sender, EventArgs e)
{
this.img = Image.FromFile(#"path");
pictureBox1.Image = img;
if (img.Height < pictureBox1.Height && img.Width < pictureBox1.Width)
{
this.pictureBox1.SizeMode = PictureBoxSizeMode.CenterImage;
}
Graphics g = Graphics.FromImage(img);
Font font=new Font("Arial",16);
SolidBrush brush = new SolidBrush(Color.Black);
brush.Color = Color.FromArgb(255, 0, 0, 255);
g.DrawString("myName", font, brush, img.Width - 178, img.Height-105);
}
private void button2_Click(object sender, EventArgs e)
{
Bitmap bitmap = new Bitmap(img);
saveJpeg(#"path", bitmap, 85L);
}
private void saveJpeg(string path, Bitmap img, long quality)
{
// Encoder parameter for image quality
EncoderParameter qualityParam =new EncoderParameter(Encoder.Quality, quality);
// Jpeg image codec
ImageCodecInfo jpegCodec = getEncoderInfo("image/jpeg");
if (jpegCodec == null)
return;
EncoderParameters encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = qualityParam;
//img.Save(path, jpegCodec, encoderParams);
img.Save(path, jpegCodec, encoderParams);
}
private ImageCodecInfo getEncoderInfo(string mimeType)
{
// Get image codecs for all image formats
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
// Find the correct image codec
for (int i = 0; i < codecs.Length; i++)
if (codecs[i].MimeType == mimeType)
return codecs[i];
return null;
}
Would you help me?
As long as the image object exists that was created by loading the image from the file, the file is in use. You can't save an image with the same name while the file is in use.
Instead of using Image.FromFile to load the image, open a file stream and use Image.FromStream to create the image, then close the file stream. That way the file is no longer in use, and you can replace it.
public ActionResult CropImage(string hdnx, string hdny, string hdnw, string hdnh)
{
string fname = "pool.jpg";
string fpath = Path.Combine(Server.MapPath("~/images"), fname);
Image oimg = Image.FromFile(fpath);
Rectangle cropcords = new Rectangle(
Convert.ToInt32(hdnx),
Convert.ToInt32(hdny),
Convert.ToInt32(hdnw),
Convert.ToInt32(hdnh));
string cfname, cfpath;
Bitmap bitMap = new Bitmap(cropcords.Width, cropcords.Height,img.PixelFormat);
Graphics grph = Graphics.FromImage(bitMap);
grph.DrawImage(oimg, new Rectangle(0, 0, bitMap.Width, bitMap.Height), cropcords, GraphicsUnit.Pixel);
cfname = "crop_" + fname;
cfpath = Path.Combine(Server.MapPath("~/cropimages"), cfname);
bitMap.Save(cfpath);
return Json("success",JsonRequestBehavior.AllowGet);
}