Re-size image in c# with specific size and dimension - c#

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.

Related

TIF Compress CCITT4

I m trying to compress tiff file with CCITT4. Original size 101 KB, after run size reduce a little 98 KB. My limit size 50 KB, how can I make the size under my limit? code is below
My limits;
Size <50KB
Image Width: 1766 – 1890 pixels
Image Length: 915 – 1040 pixels
Thanks everyone.
Image Sample
public static Byte[] CompressBitmap(Bitmap img)
{
Bitmap bm = img;
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
ImageCodecInfo ici = null;
foreach (ImageCodecInfo codec in codecs)
{
if (codec.MimeType == "image/tiff")
ici = codec;
}
EncoderParameters ep = new EncoderParameters(1);
ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.SaveFlag,
(long)EncoderValue.Flush);
ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Compression,
(long)EncoderValue.CompressionCCITT4);
using (var stream = new MemoryStream())
{
bm.Save(stream, ici, ep);
return stream.ToArray();
}
}
Since the source Image seems to be a Color Image (that just happens to look like Black&White) and its dimensions need to be adjusted to a maximum value, you need to:
Redefine proportionally the Size of the source Image, setting the maximum Width and Height limits that the converted image needs to respect
Convert the Image to a 1bpp Indexed format: the Bitmap.Clone() method provides means to both define the source Image new Size and set a specific PixelFormat (PixelFormat.Format1bppIndexed, in this case). The internal conversion method is quite good, the resulting Image quality is usually a good compromise.
Define the quality of the Image: since the Image will be converted to B&W, its quality can be set initially to the maximum (100). The process can be repeated if the final Image bytes count doesn't fit the requirements (50KB)
Compress the Image using the CCTII Group 4 Compression scheme
Encode the Image, using the TIFF format Encoder, saving it to a MemoryStream for further processing
The size in bytes of the sample Image provided in the question, when saved to a file, is reduced from the initial 94,600 bytes to 6,786 bytes with maximum quality.
File.WriteAllBytes("[Image Path].tiff", imageStream.ToArray());
The ConvertToBWTiffImageStream() method can be called as:
var maximumWidthAndHeight = new Size(1024, 768);
int quality = 100;
var compression = EncoderValue.CompressionCCITT4;
var imageStream = ConvertToBWTiffImageStream(sourceImage, maximumWidthAndHeight, quality, compression);
Note that maximumWidthAndHeight specify the maximum Width and the maximum Height, not a new Size of the Image (which would be otherwise distorted). The Image Size is scaled proportionally according to these values.
► Remember to add this: using ImageCodec = System.Drawing.Imaging.Encoder;
(the GetImageMaxSize() method is half-dumb, I'll fix it later)
using System.Drawing;
using System.Drawing.Imaging;
using ImageCodec = System.Drawing.Imaging.Encoder;
private MemoryStream ConvertToBWTiffImageStream(Bitmap image, Size maxWidthHeight, long compressionLevel, EncoderValue compressionScheme)
{
var imageSize = GetImageMaxSize(image, maxWidthHeight);
var bwImage = image.Clone(new Rectangle(Point.Empty, image.Size), PixelFormat.Format1bppIndexed);
compressionLevel = Math.Max(0, Math.Min(100, compressionLevel));
var codecParms = new EncoderParameters(2);
try {
var imageCodec = ImageCodecInfo.GetImageEncoders().FirstOrDefault(enc => enc.FormatID == ImageFormat.Tiff.Guid);
var qualityParam = new EncoderParameter(ImageCodec.Quality, compressionLevel);
var compressionParam = new EncoderParameter(ImageCodec.Compression, (long)compressionScheme);
var ms = new MemoryStream();
codecParms.Param[0] = qualityParam;
codecParms.Param[1] = compressionParam;
image.Save(ms, imageCodec, codecParms);
return ms;
}
finally {
codecParms.Dispose();
bwImage.Dispose();
}
}
private Size GetImageMaxSize(Image image, Size maxSize)
{
float scale = 1.0f;
if (image.Width > maxSize.Width && image.Width >= image.Height) {
scale = (float)maxSize.Width / image.Width;
}
else if (image.Height > maxSize.Height) {
scale = (float)maxSize.Height / image.Height;
}
return new Size((int)(image.Width * scale), (int)(image.Height * scale));
}

My image is not compressing

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);
}

How compress image without change the original height ,width of image in c#

I want to make image size smaller then its original size.I am using following code for compress the size images but it increased the image size from 1MB to 1.5MB
Any Other solution for compress large size images without change image original height,width.
public static byte[] CompressImage(Image img) {
int originalwidth = img.Width, originalheight = img.Height;
Bitmap bmpimage = new Bitmap(originalwidth, originalheight);
Graphics gf = Graphics.FromImage(bmpimage);
gf.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
gf.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.AssumeLinear;
gf.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
Rectangle rect = new Rectangle(0, 0, originalwidth, originalheight);
gf.DrawImage(img, rect, 0, 0, originalwidth, originalheight, GraphicsUnit.Pixel);
byte[] imagearray;
using (MemoryStream ms = new MemoryStream())
{
bmpimage.Save(ms, ImageFormat.Jpeg);
imagearray= ms.ToArray();
}
return imagearray;
}
You can set the quality level when you save the file as JPEG, which mostly also directly will correlate with file size - the less quality the smaller your output file will be.
Also see How to: Set JPEG Compression Level , for an example see this SO answer.
As said by #BrokenGlass you can specify the compression level within the EncoderParameter. Here's a snippet if you want to give a try changing quality:
public static void SaveJpeg(string path, Image image, int quality)
{
//ensure the quality is within the correct range
if ((quality < 0) || (quality > 100))
{
//create the error message
string error = string.Format("Jpeg image quality must be between 0 and 100, with 100 being the highest quality. A value of {0} was specified.", quality);
//throw a helpful exception
throw new ArgumentOutOfRangeException(error);
}
//create an encoder parameter for the image quality
EncoderParameter qualityParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
//get the jpeg codec
ImageCodecInfo jpegCodec = GetEncoderInfo("image/jpeg");
//create a collection of all parameters that we will pass to the encoder
EncoderParameters encoderParams = new EncoderParameters(1);
//set the quality parameter for the codec
encoderParams.Param[0] = qualityParam;
//save the image using the codec and the parameters
image.Save(path, jpegCodec, encoderParams);
}

Making image thumbnail smaller

I am having image sharing application where users upload images and I take thumbnails of these images...how ever , everything is working fine but sometimes the image thumbnail(600 * 800) size is almost 1 mb which is very huge is there anyway to modify the image resolution or something to make the size like..100 kb or something . this is my code .
Bitmap bmp = new Bitmap(Width, Height);
System.Drawing.Graphics gr = System.Drawing.Graphics.FromImage(bmp);
gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
gr.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
System.Drawing.Rectangle rectDestination = new System.Drawing.Rectangle(0, 0, Width, Height);
System.Drawing.Size rs = new System.Drawing.Size();
rs.Height = Height;
rs.Width = Width;
gr.DrawImage(originalImage, new Rectangle(new Point(0, 0), rs), 0, 0, originalImage.Width, originalImage.Height, GraphicsUnit.Pixel);
string thumbnailPath = string.Concat(pathToSaveIn, thumbnailName);
bmp.Save(thumbnailPath);
gr.Dispose();
The image resizing code looks OK (at first glance). However, you're saving the image in bitmap format, which is lossless -- hence the large size of the file.
You probably want to use JPEG instead for a thumbnail: for photographs, etc., this gives good compression.
This may help:
public void SaveImage(Bitmap image, string filename)
{
long quality = 80L; // adjust as appropriate
var qualityEncoder = Encoder.Quality;
using (var encoderParameter = new EncoderParameter(qualityEncoder, quality))
using (var encoderParams = new EncoderParameters(1))
{
encoderParams.Param[0] = encoderParameter;
var jpegEncoder = GetEncoder(ImageFormat.Jpeg);
image.Save(filename, jpegEncoder, encoderParams);
}
}
private static ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
return codecs
.Where(codec => codec.FormatID == format.Guid)
.FirstOrDefault();
}
This looks like .NET. Scott Hanselman had a good blog post on this. Essentially a review of a package on NuGet that helps with this.
http://www.hanselman.com/blog/NuGetPackageOfWeek11ImageResizerEnablesCleanClearImageResizingInASPNET.aspx
Try System.Drawing.Image.GetThumbnailImage. I haven't used it myself, but looks like it might work.

C# change dpi of an uploaded image

I've got to following function which is called to change the resolution of an image. I want to do this so uploaded image with for example 300dpi will be modified to 72dpi (for web). This question is related to another question here on SO where i'm working on.
I'm creation an extension method for this to be able to use this function on more places in my application, instead of only when uploading new files. (See above mentioned question)
public static byte[] SetDpiTo72(this byte[] imageToFit, string mimeType, Size newSize)
{
using (MemoryStream memoryStream = new MemoryStream(), newMemoryStream = new MemoryStream())
{
memoryStream.Write(imageToFit, 0, imageToFit.Length);
var originalImage = new Bitmap(memoryStream);
using (var canvas = Graphics.FromImage(originalImage))
{
canvas.SmoothingMode = SmoothingMode.AntiAlias;
canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
canvas.PixelOffsetMode = PixelOffsetMode.HighQuality;
canvas.DrawImage((Image)originalImage,0,0, newSize.Width, newSize.Height);
newBitmap.SetResolution(72, 72);
newBitmap.Save(newMemoryStream, ImageFunctions.GetEncoderInfo(mimeType), null);
}
return newMemoryStream.ToArray();
}
}
The mentioned extension methode is being called in a function similar to the situation below;
if (newSize.Width > originalImage.Width && newSize.Height > originalImage.Height)
{
newSize.Width = originalImage.Width;
newSize.Height = originalImage.Height;
uploadedFileBuffer = uploadedFileBuffer.SetDpiTo72(uploadedFile.ContentType, newSize);
return CreateFile(newSize, uploadedFile, uploadedFileBuffer);
}
The bytearray coming in is the file as an bytearray. It already has the correct size, but I want to change the resolution to 72dpi. However after exectution and saving the image the resolution is still the originale entered resolution, which is 300dpi. How can I do this?
UPDATE AFTER SEVERAL ANSWERS:
public static byte[] SetDpiTo72(this byte[] imageToFit, string mimeType, Size newSize)
{
using (MemoryStream memoryStream = new MemoryStream(), newMemoryStream = new MemoryStream())
{
memoryStream.Write(imageToFit, 0, imageToFit.Length);
var originalImage = new Bitmap(memoryStream);
using (var canvas = Graphics.FromImage(originalImage))
{
canvas.SmoothingMode = SmoothingMode.AntiAlias;
canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
canvas.PixelOffsetMode = PixelOffsetMode.HighQuality;
canvas.DrawImage((Image)originalImage,0,0, newSize.Width, newSize.Height);
originalImage.SetResolution(72, 72);
var epQuality = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 75);
var epParameters = new EncoderParameters(1);
epParameters.Param[0] = epQuality;
Image newimg = Image.FromStream(memoryStream);
//Getting an GDI+ exception after the execution of this line.
newimg.Save("C:\\test1234.jpg", ImageFunctions.GetEncoderInfo(mimeType), epParameters);
originalImage.Save("test.jpg", ImageFormat.Jpeg);
//This line give me an Argumentexception - Parameter is not valid.
//originalImage.Save(newMemoryStream, ImageFunctions.GetEncoderInfo(mimeType), epParameters);
//newMemoryStream.Close();
}
return newMemoryStream.ToArray();
}
}
The stackstrace which comes with the exception is telling me the following;
at System.Drawing.Image.Save(String filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
at Extensions.ByteArrayExtensions.SetDpiTo72(Byte[] imageToFit, String mimeType, Size newSize) in C:\Website\Project\Extensions\ByteArrayExtensions.cs:line 356
at CMS.Presentation.FileFunctions.CreateFullsizeImage(HttpPostedFileBase uploadedFile, Size newSize, Byte[] uploadedFileBuffer) in C:\Website\Project\CMS.Presentation\FileFunctions.cs:line 197
at CMS.Presentation.FileFunctions.CreateFile(HttpPostedFileBase uploadedFile, INodeService nodeservice, Guid userId, Node parentNode) in C:\Website\Project\CMS.Presentation\FileFunctions.cs:line 53
In the mean time I've also developed another function (see below) resizing just a bitmap. And this seem to work correctly. I can't use this function with my current implementation though because it returns just an Bitmap. Or should i change everything to work with bitmaps?
private static Bitmap ResizeImage(Image image, int width, int height)
{
var frameCount = image.GetFrameCount(new FrameDimension(image.FrameDimensionsList[0]));
var newDimensions = ImageFunctions.GenerateImageDimensions(image.Width, image.Height, width, height);
Bitmap resizedImage;
if (frameCount > 1)
{
//we have a animated GIF
resizedImage = ResizeAnimatedGifImage(image, width, height);
}
else
{
resizedImage = (Bitmap)image.GetThumbnailImage(newDimensions.Width, newDimensions.Height, null, IntPtr.Zero);
}
resizedImage.SetResolution(72,72);
return resizedImage;
}
Ok, I tried it only on files on harddrive, but it should work with streams too.
Bitmap bitmap = new Bitmap(loadFrom);
Bitmap newBitmap = new Bitmap(bitmap);
newBitmap.SetResolution(72, 72);
newBitmap.Save(saveTo);
Took me a while, but I finally found the problem!
The problem lied in the ResizeImage function I used. In the 'GetThumbnailImage' to be specific. I ran into another problem with blurry images, which was explainable because GetThumbnailImage would stretch up the created ThumbNail to the desired size. And the resolution off the thumbnail never changes.
private static Bitmap ResizeImage(Image image, int width, int height)
{
var frameCount = image.GetFrameCount(new FrameDimension(image.FrameDimensionsList[0]));
var newDimensions = ImageFunctions.GenerateImageDimensions(image.Width, image.Height, width, height);
Bitmap resizedImage;
if (frameCount > 1)
{
//we have a animated GIF
resizedImage = ResizeAnimatedGifImage(image, width, height);
}
else
{
resizedImage = (Bitmap)image.GetThumbnailImage(newDimensions.Width, newDimensions.Height, null, IntPtr.Zero);
}
resizedImage.SetResolution(72,72);
return resizedImage;
}
By modifying the function above to the function below I was able to solve the problem using Graphics.DrawImage to redraw the new image before rendering it. Also the GenerateImageDimensions was slightly modified. This taken together the problem was solved.
private static Bitmap ResizeImage(Image image, int width, int height)
{
var frameCount = image.GetFrameCount(new FrameDimension(image.FrameDimensionsList[0]));
var newDimensions = ImageFunctions.GenerateImageDimensions(image.Width, image.Height, width, height);
var resizedImage = new Bitmap(newDimensions.Width, newDimensions.Height);
if (frameCount > 1)
{
//we have a animated GIF
resizedImage = ResizeAnimatedGifImage(image, width, height);
}
else
{
//we have a normal image
using (var gfx = Graphics.FromImage(resizedImage))
{
gfx.SmoothingMode = SmoothingMode.HighQuality;
gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
gfx.PixelOffsetMode = PixelOffsetMode.HighQuality;
var targRectangle = new Rectangle(0, 0, newDimensions.Width, newDimensions.Height);
var srcRectangle = new Rectangle(0, 0, image.Width, image.Height);
gfx.DrawImage(image, targRectangle, srcRectangle, GraphicsUnit.Pixel);
}
}
return resizedImage;
}
By "changing the resolution", do you actually mean you want to reduce the number of pixels in the image by 72/300? I.e. change a 4000x3000 image to 960x720?
If so, I can't see where your code actually does that. The overload of DrawImage() you're using does this:
Draws the specified image, using its original physical size, at the location specified by a coordinate pair.
Which is exactly what is happening.
Try one of the other overloads such as this one:
Draws the specified Image at the specified location and with the specified size.
for example:
// Create image.
Image newImage = Image.FromFile("SampImag.jpg");
// Create coordinates for upper-left corner of image and for size of image.
int x = 0;
int y = 0;
int width = 450;
int height = 150;
// Draw image to screen.
e.Graphics.DrawImage(newImage, x, y, width, height);
EDIT: per the comments, I understand the OP wants to reduce file size without reducing pixel count. Therefore the files must be recompressed.
I've borrowed some sample code from here:
ImageCodecInfo iciJpegCodec = null;
// This will specify the image quality to the encoder. Change the value of 75 from 0 to 100, where 100 is best quality, but highest file size.
EncoderParameter epQuality = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 75);
// Get all image codecs that are available
ImageCodecInfo[] iciCodecs = ImageCodecInfo.GetImageEncoders();
// Store the quality parameter in the list of encoder parameters
EncoderParameters epParameters = new EncoderParameters(1);
epParameters.Param[0] = epQuality;
// Loop through all the image codecs
for (int i = 0; i < iciCodecs.Length; i++)
{
// Until the one that we are interested in is found, which is image/jpeg
if (iciCodecs[i].MimeType == "image/jpeg")
{
iciJpegCodec = iciCodecs[i];
break;
}
}
// Create a new Image object from the current file
Image newImage = Image.FromFile(strFile);
// Get the file information again, this time we want to find out the extension
FileInfo fiPicture = new FileInfo(strFile);
// Save the new file at the selected path with the specified encoder parameters, and reuse the same file name
newImage.Save(outputPath + "\\" + fiPicture.Name, iciJpegCodec, epParameters);
Rob, I believe that issue with your code is at saving the image - the actual digital image data would be certain number of dots/pixels i.e. (m x n) and setting resolution at bitmap wouldn't/shouldn't change the number dots (and hence physical byte size of image). The resolution information will be stored in the image header (to be used by programs while printing/editing images) - what happens if you store the new bitmap to file instead of mem stream
newBitmap.Save("c:\test.png", ImageFormat.Png);
Check dpi for above file from file -> properties -> summary (advanced). It should be 72 dpi.

Categories

Resources