Why does the PngBitmapEncoder class make my images look grainy? - c#

I'm using this code to convert a jpg image into a png 8.
The code works but the image looks grainy. I did an export in Photoshop as png 8 and it looks smoother and no grain.
Note: 8-bit png
Any image gurus out there that can help?
My code:
Image ImageFile = Image.FromFile(#"C:\Documents and Settings\dvela\My Documents\Downloads\pngtest\Batman01.jpg");
Rectangle NewSize = new Rectangle();
NewSize.Width = 112;
NewSize.Height = (NewSize.Width * ImageFile.Height) / ImageFile.Width;
Bitmap image = new Bitmap(NewSize.Width, NewSize.Height);
Graphics graphics = Graphics.FromImage(image);
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.DrawImage(ImageFile, NewSize);
FormatConvertedBitmap fcb = new FormatConvertedBitmap(System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(image.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromWidthAndHeight(NewSize.Width, NewSize.Height)), PixelFormats.Indexed8, BitmapPalettes.Halftone256Transparent, 0.5);
PngBitmapEncoder pngBitmapEncoder = new PngBitmapEncoder();
pngBitmapEncoder.Interlace = PngInterlaceOption.Off;
pngBitmapEncoder.Frames.Add(BitmapFrame.Create(fcb));
using (Stream fileStream = File.Open(#"C:\Documents and Settings\dvela\My Documents\Downloads\pngtest\test.png", FileMode.Create))
{
pngBitmapEncoder.Save(fileStream);
}

You are using a fixed palette with 256 colors for your converted image. Photoshop probably uses a palette that is created specially for that image and contains all of it's colors (or most of them).
You could use dithering, or somehow generate a custom palette, or both.
some more information is also here.

I found this library that does Palette-based and Octree-based color Quantization.
This MSDN article explains the difference between the algorithms with code.

Related

In .Net, while converting "Image" with another image , then one of the image's color quality is affected

In .net application when the image is processed & merged with another image then its colours are affected.
Image image ---------> Uploaded image
Bitmap xy = new Bitmap(image, image.Width, image.Height); -----> Conversion into Bitmap to perform various operations like resize or merge with another image.
So the “image” object has few “PropertyItems” and when we convert image to Bitmap type of object then these “PropertyItems” array is empty, which means these “PropertyItems” were not moved in this conversion.
Now after this image is moved into Bitmap object for merging with another image, then “PropertyItems” array is empty
Due to loss of these propertyitems, color of the image is changed.
For merging, I'm using below code
public string MergeImages(List<BlockPositionDetailsWithSize> blockPositionDetailsWithSize, int layoutWidth, int layoutHeight)
{
var bitmap = new Bitmap(layoutWidth-75, layoutHeight);
float width = 0, height = 0;
using (var g = Graphics.FromImage(bitmap))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
var bitmapImg = blockPositionDetailsWithSize.Where(b => b.BlockId.Contains("fImageBlock")).FirstOrDefault();
// This loop is placing two images in graphic object
foreach (var block in blockPositionDetailsWithSize)
{
width = block.Width;
height = block.Height;
g.DrawImage(block.BlockImage, block.PosX, block.PosY, block.Width, block.Height);
}
}
ImageCodecInfo jpgEncoder = GetEncoder(ImageFormat.Jpeg); // change image format
System.IO.MemoryStream ms = new System.IO.MemoryStream();
bitmap.SetResolution(288, 288);
System.Drawing.Imaging.Encoder imgEncoder = System.Drawing.Imaging.Encoder.Quality;
EncoderParameters imgEncoderParameters = new EncoderParameters(1);
EncoderParameter imgEncoderParameter = new EncoderParameter(imgEncoder, 95L); //A quality level of 0 corresponds to the greatest compression, and a quality level of 100 corresponds to the least compression.
imgEncoderParameters.Param[0] = imgEncoderParameter;
bitmap.Save(ms, jpgEncoder, imgEncoderParameters);
byte[] byteImage = ms.ToArray();
string base64String = string.Empty;
base64String = Convert.ToBase64String(byteImage); ////Get Base64
return base64String;
}
Image's color quality can be maintained by following code
foreach (System.Drawing.Imaging.PropertyItem item in bitmapImg.BlockImage.PropertyItems)
{
try
{
bitmap.SetPropertyItem(item);
}
catch
{
}
}
Now the issue is I cannot apply one image's property item on the final image(produced after merging). Because when I apply one image's property items onto another image then the second image's color quality is affected but if I don't apply then the first Image's color quality is affected.
So I am looking for the way to merge two images without loosing of any it's property items.

Why does my image resize code always product a completely black output image?

I'm trying to resize an image. I thought it was a simple task...
Here's my code (note, the two Save calls are just for debugging to illustrate the problem):
var newSize = new Size { Width = 450, Height = 250 };
using (var img = (Bitmap)Image.FromFile(sourceImageFilename))
{
var outputImage = new Bitmap(newSize.Width, newSize.Height);
// Save input image for debugging (screenshot below)
img.Save(#"M:\Coding\Photos\Temp\input.jpg");
using (Graphics gr = Graphics.FromImage(img))
{
gr.SmoothingMode = SmoothingMode.HighQuality;
gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
gr.DrawImage(outputImage, new Rectangle(0, 0, newSize.Width, newSize.Height));
}
// Save output image for debugging (screenshot below)
outputImage.Save(#"M:\Coding\Photos\Temp\output.jpg");
}
This appears to be the exact same code a ton of people are using (and exists on SO in many answers). However, here's what the two images that are being written to disk look like:
The original image is 5344x3006 and newSize (and the black output image) are 450x250.
All my other code is working fine (reading pixels from the input image with SetPixel, etc.), it's just this resize that's broken. Doing the resize with the Bitmap constructor is fine (but a bad quality resize).
You need to get the graphics from the OutputImage.
public static Bitmap Scale(this Bitmap inputImage, Size newSize)
{
var outputImage = new Bitmap(newSize.Width, newSize.Height);
inputImage.Save(#"M:\Coding\Photos\Temp\input.jpg");
using (Graphics gr = Graphics.FromImage(outputImage))
{
gr.SmoothingMode = SmoothingMode.HighQuality;
gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
gr.DrawImage(inputImage, new Rectangle(0, 0, newSize.Width, newSize.Height));
}
outputImage.Save(#"M:\Coding\Photos\Temp\output.jpg");
return outputImage;
}

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.

Alpha channel transparency and resizing image files

I'm using the following code to resize a tif. The tif has an alpha channel set for transparency. I'm trying to resize this image and honour the transparency but at the moment it's coming out with a black background. Any ideas?
public static void ResizeImage(string OriginalImagePath, string NewImagePath, int Width, int Height)
{
Size NewSize = new Size(Width, Height);
using (Image OriginalImage = Image.FromFile(OriginalImagePath))
{
//Graphics objects can not be created from bitmaps with an Indexed Pixel Format, use RGB instead.
PixelFormat Format = OriginalImage.PixelFormat;
if (Format.ToString().Contains("Indexed"))
Format = PixelFormat.Format24bppRgb;
using (Bitmap NewImage = new Bitmap(NewSize.Width, NewSize.Height, OriginalImage.PixelFormat))
{
using (Graphics Canvas = Graphics.FromImage(NewImage))
{
Canvas.SmoothingMode = SmoothingMode.AntiAlias;
Canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
Canvas.PixelOffsetMode = PixelOffsetMode.HighQuality;
Canvas.DrawImage(OriginalImage, new Rectangle(new Point(0, 0), NewSize));
NewImage.Save(NewImagePath, OriginalImage.RawFormat);
}
}
}
}
}
Try this:
if (Format.ToString().Contains("Indexed"))
Format = PixelFormat.Format32bppArgb;
Format32bppArgb specifies an alpha channel in the pixel format.
And I think you meant to do this:
using (Bitmap NewImage = new Bitmap(NewSize.Width, NewSize.Height, Format))
EDIT:
Actually, try just forcing the pixel format on the NewImage to Format32bppArgb like so:
using (Bitmap NewImage = new Bitmap(NewSize.Width, NewSize.Height,
PixelFormat.Format32bppArgb))
Canvas.Clear(Color.Transparent)
before you blit.
I actually found out that due to the way the transparency is stored in the tiff format with photoshop it was better to create png's by automating photoshop and then crunching off the resulting png.

C# generating thumbnails filesize

I have the following code to take an image and generate the thumbnail.
how can I alter the quality or compression to get smaller file sizes programatically?
Image thumbNail = image.GetThumbnailImage(Width, Height, null, new IntPtr());
If you truly need better control over the thumbnails produced, you will be better off producing your own by manually generating an image of smaller size and different quality. The GetThumbnailImage dows not give you much control.
See this article for how it's done.
http://www.switchonthecode.com/tutorials/csharp-tutorial-image-editing-saving-cropping-and-resizing
When saving the thumbNail with Image.Save you can specify the quality by passing a EncoderParameter. See: Reducing JPEG Picture Quality using C#
EncoderParameter epQuality = new EncoderParameter(
System.Drawing.Imaging.Encoder.Quality,
(int)numQual.Value);
...
newImage.Save(..., iciJpegCodec, epParameters);
You do not use the GetThumbnailImage API:
protected Stream ResizeImage(string source, int width, int height) {
using (System.Drawing.Bitmap bmp = (System.Drawing.Bitmap)System.Drawing.Bitmap.FromFile(source))
using (System.Drawing.Bitmap newBmp = new System.Drawing.Bitmap(width, height))
using (System.Drawing.Graphics graphic = System.Drawing.Graphics.FromImage(newBmp))
{
graphic.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphic.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
graphic.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
graphic.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphic.DrawImage(bmp, 0, 0, width, height);
MemoryStream ms = new MemoryStream();
newBmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
return ms;
}
}

Categories

Resources