C# Creating thumbnail (low quality and big size problem) - c#

public void CreateThumbnail(Image img1, Photo photo, string targetDirectoryThumbs)
{
int newWidth = 700;
int newHeight = 700;
double ratio = 0;
if (img1.Width > img1.Height)
{
ratio = img1.Width / (double)img1.Height;
newHeight = (int)(newHeight / ratio);
}
else
{
ratio = img1.Height / (double)img1.Width;
newWidth = (int)(newWidth / ratio);
}
Image bmp1 = img1.GetThumbnailImage(newWidth, newHeight, null, IntPtr.Zero);
bmp1.Save(targetDirectoryThumbs + photo.PhotoID + ".jpg");
img1.Dispose();
bmp1.Dispose();
}
I've put 700px so that you can have better insight of the problem.
Here is original image and resized one.
Any good recommendation?
Thanks,
Ile

You should find my answer to this question helpful. It includes a sample for high quality image scaling in C#.
The full sample in my other answer includes how to save the image as a jpeg.
Here's the relevant bit of code...
/// <summary>
/// Resize the image to the specified width and height.
/// </summary>
/// <param name="image">The image to resize.</param>
/// <param name="width">The width to resize to.</param>
/// <param name="height">The height to resize to.</param>
/// <returns>The resized image.</returns>
public static System.Drawing.Bitmap ResizeImage(System.Drawing.Image image, int width, int height)
{
//a holder for the result
Bitmap result = new Bitmap(width, height);
//use a graphics object to draw the resized image into the bitmap
using (Graphics graphics = Graphics.FromImage(result))
{
//set the resize quality modes to high quality
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//draw the image into the target bitmap
graphics.DrawImage(image, 0, 0, result.Width, result.Height);
}
//return the resulting bitmap
return result;
}

If the image contains a thumbnail it'll automatically stretch it to the desired size...which will make it look like crap (like your case ;))
Straight outta MSDN...
If the Image contains an embedded
thumbnail image, this method retrieves
the embedded thumbnail and scales it
to the requested size. If the Image
does not contain an embedded thumbnail
image, this method creates a thumbnail
image by scaling the main image.
In your case I just double checked the source image for it's thumbnail and got this...
New Windows Thumbnail : JPEG Format (Offset:830Size:3234)
Thumbnail Type : JPEG
Thumnail Width : 112
Thumbnail Height : 84

Try to draw the original image to another smaller image, and save the result.
Bitmap bmp1 = new Bitmap(newWidth, newHeight);
Graphics g = Graphics.FromImage(bmp);
g.DrawImage(img1, 0, 0, newWidth, newHeight);
bmp1.Save(targetDirectoryThumbs + photo.PhotoID + ".jpg", ImageFormat.Jpeg);

Are you allowed to use third party applications? If so, you may want to check out ImageMagick to manage thumbnail creation. There's a .NET wrapper.
http://imagemagick.codeplex.com/

I wrote a free .dll that does this easily. It's here if you want to see the documentation....
Git Repository & Tutorial

Related

Loosing transparency in System.Drawing.Image when using ImageResizer for resizing

For my current WPF appliaction I have to down scale some System.Drawing.Image ( objects which I load from PNG files (some of them with transparent background). I've tried multiple approaches for the resizing of the images and all of them worked fine in terms of having a smaller image afterwards. But unfortunately they all make the images loose their transparency.
My last try was to use ImageResizer an external library to get the job done as I expected it to handle that problem easily, but I still have the same issue: Original image is displayed with transparent background; Resized image displayed is displayed with black background.
Here's my code for the usage of the ImageResizer library:
ImageResizer.Instructions inst = new ImageResizer.Instructions("width=" + newWidth.ToString() + ";height=" + newHeight.ToString() + ";format=png;mode=max");
ImageResizer.ImageJob job = new ImageResizer.ImageJob(originalImage, typeof(System.Drawing.Bitmap), inst);
job.Build();
return job.Result as System.Drawing.Image;
These are my other two approaches, which also basically deliver the same result (Image resized: yet; Transparency preserved: Nope):
return originalImage.GetThumbnailImage(newWidth, newHeight, null, IntPtr.Zero); // Transparency gets lost
return new System.Drawing.Bitmap(originalImage, new System.Drawing.Size(newWidth, newHeight)); // Transparency gets lost
Any ideas on what I have to do in order preserve the transparency while resizing?
Regards
Ralf
ImageResizer always preserves transparency.
You're losing transparency during the encoding (or display) of the image, which happens after you've taken control away from ImageResizer. Instead of passing in typeof(System.Drawing.Bitmap), pass in an output path or output stream.
var i = new Instructions(){ Width = newWidth,Height = newHeight, OutputFormat= OutputFormat.Png, Mode= FitMode.Max};
new ImageJob(originalImage, "output.png", i).Build();
ImageResizer can't control how an image is encoded if you take a raw Bitmap from it instead.
You didn't specific how you're using the results, which is very important. If you are not writing them to disk or a stream, but are instead displaying them, then you're looking for the problem in the wrong place. It's likely that the code responsible for compositing the results onto the display surface is failing to treat the image as a 32-bit image, and is instead ignoring the alpha channel.
Even though you're using WPF, you're working with System.Drawing.Image objects, so you can do this:
public static Bitmap ResizeImage(Image imgToResize, int newHeight)
{
int sourceWidth = imgToResize.Width;
int sourceHeight = imgToResize.Height;
float nPercentH = ((float)newHeight / (float)sourceHeight);
int destWidth = Math.Max((int)Math.Round(sourceWidth * nPercentH), 1); // Just in case;
int destHeight = newHeight;
Bitmap b = new Bitmap(destWidth, destHeight);
using (Graphics g = Graphics.FromImage((Image)b))
{
g.SmoothingMode = SmoothingMode.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
}
return b;
}
Afterwards, be sure to save it with the PNG encoder:
public static System.Drawing.Imaging.ImageCodecInfo GetEncoder(System.Drawing.Imaging.ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
and then
codec = GetEncoder(ImageFormat.Png);
newBitmap.Save(newFile, codec, null);
(Note I'm using the standard .Net class libraries rather than a 3rd party library; hope that's OK.)
Update
Incidentally, since you are working in WPF, why not use WPF's image manipulation?
public static class BitmapHelper
{
public static void SaveToPng(this BitmapSource bitmap, string fileName)
{
var encoder = new PngBitmapEncoder();
SaveUsingEncoder(bitmap, fileName, encoder);
}
public static void SaveUsingEncoder(this BitmapSource bitmap, string fileName, BitmapEncoder encoder)
{
BitmapFrame frame = BitmapFrame.Create(bitmap);
encoder.Frames.Add(frame);
using (var stream = File.Create(fileName))
{
encoder.Save(stream);
}
}
public static void ImageLoadResizeAndSave(string inFile, string outFile, int newPixelHeight)
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.UriSource = new Uri(inFile);
image.EndInit();
var newImage = BitmapHelper.ResizeImageToHeight(image, newPixelHeight);
BitmapHelper.SaveToPng(newImage, outFile);
}
/// <summary>
/// Resize the image to have the selected height, keeping the width proportionate.
/// </summary>
/// <param name="imgToResize"></param>
/// <param name="newHeight"></param>
/// <returns></returns>
public static BitmapSource ResizeImageToHeight(BitmapSource imgToResize, int newPixelHeight)
{
double sourceWidth = imgToResize.PixelWidth;
double sourceHeight = imgToResize.PixelHeight;
var nPercentH = ((double)newPixelHeight / sourceHeight);
double destWidth = Math.Max((int)Math.Round(sourceWidth * nPercentH), 1); // Just in case;
double destHeight = newPixelHeight;
var bitmap = new TransformedBitmap(imgToResize, new ScaleTransform(destWidth / imgToResize.PixelWidth, destHeight / imgToResize.PixelHeight));
return bitmap;
}
}
Maybe you're losing transparencies converting images in the older format to WPF format?

How to pass the value in image cell in iTextSharp?

Based on below Part of code, May i know how to passing the picturebox image in image cell iTextSharp in C# ? kindly advise. thank you
Image<Bgr, Byte> img1 = new Image<Bgr, Byte>(Application.StartupPath + "/TrainedFaces/" + reader.GetString(11) + ".bmp");
Application.Idle -= new EventHandler(ProcessFrame);
pictureBox1.Image = img1;
Shall I use string format ?
cell = ImageCell(string.Format("+ img1 +"), 25f, PdfPCell.ALIGN_CENTER);
ImageCell takes byte[] data, it won't take string.
Here is the entire implementation,
// Code resides in your page codebehind, do not confuse with AppLevel, it's just my static class
System.Drawing.Image imgResized = AppLevel.Resize(AppLevel.byteArrayToImage(File.ReadAllBytes(Server.MapPath("~/images/companylogo.jpg"))), 194, 138, RotateFlipType.RotateNoneFlipNone);
cell = AppLevel.ImageCell(AppLevel.imageToByteArray(imgResized), 30f, PdfPCell.ALIGN_RIGHT);
table.AddCell(cell);
///Helper methods///
/// <summary>
/// Resizes and rotates an image, keeping the original aspect ratio. Does not dispose the original
/// Image instance.
/// </summary>
/// <param name="image">Image instance</param>
/// <param name="width">desired width</param>
/// <param name="height">desired height</param>
/// <param name="rotateFlipType">desired RotateFlipType</param>
/// <returns>new resized/rotated Image instance</returns>
public static System.Drawing.Image Resize(System.Drawing.Image image, int width, int height, RotateFlipType rotateFlipType)
{
// clone the Image instance, since we don't want to resize the original Image instance
var rotatedImage = image.Clone() as System.Drawing.Image;
rotatedImage.RotateFlip(rotateFlipType);
var newSize = CalculateResizedDimensions(rotatedImage, width, height);
var resizedImage = new Bitmap(newSize.Width, newSize.Height, PixelFormat.Format32bppArgb);
resizedImage.SetResolution(72, 72);
using (var graphics = Graphics.FromImage(resizedImage))
{
// set parameters to create a high-quality thumbnail
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
// use an image attribute in order to remove the black/gray border around image after resize
// (most obvious on white images), see this post for more information:
// http://www.codeproject.com/KB/GDI-plus/imgresizoutperfgdiplus.aspx
using (var attribute = new ImageAttributes())
{
attribute.SetWrapMode(WrapMode.TileFlipXY);
// draws the resized image to the bitmap
graphics.DrawImage(rotatedImage, new System.Drawing.Rectangle(new Point(0, 0), newSize), 0, 0, rotatedImage.Width, rotatedImage.Height, GraphicsUnit.Pixel, attribute);
}
}
return resizedImage;
}
public static System.Drawing.Image byteArrayToImage(byte[] byteArrayIn)
{
MemoryStream ms = new MemoryStream(byteArrayIn);
System.Drawing.Image returnImage = System.Drawing.Image.FromStream(ms);
eturnImage;
}

Apply Curl effect to a static Image C#

Anyone ever wrote a C# GDI+ function to curl the corner of a BITMAP. I need to be able to take a static image and apply the peel effect to it on the bottom right corner. And I need to do it with C# all my searching leads to CSS3/FLASH/SilverLight virtual book type examples. I just want to create a static image that has a curled up corner and save the file.
Any Ideas?
Ok so I made the image with photo shop so that I can show you what I am trying to achieve
I start this image
and I want to write some C# code that would yield this image
The end result is just an image that is not animated, and does nothing. Any thoughts.
There are some good tools to do this, such as Fred's ImageMagick plugin script, but here is a C# version as requested.
using System.Drawing.Imaging;
public partial class ImagePeelEffect : Form
{
string WorkingDirectory = #"C:\temp\";
public ImagePeelEffect()
{
InitializeComponent();
}
private void ImagePeelEffect_Load(object sender, EventArgs e)
{
picBefore.Image = Image.FromFile(WorkingDirectory + "\\before.jpg");
}
private void button1_Click(object sender, EventArgs e)
{
//create a image object containing the photograph to add page peel effect
Image imgPhoto = Image.FromFile(WorkingDirectory + "\\before.jpg");
int phWidth = imgPhoto.Width;
int phHeight = imgPhoto.Height;
//create a Bitmap the Size of the original photograph
Bitmap bmPhoto = new Bitmap(phWidth, phHeight, PixelFormat.Format24bppRgb);
bmPhoto.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution);
//load the Bitmap into a Graphics object
Graphics grPhoto = Graphics.FromImage(bmPhoto);
//create a image object containing the PagePeel
Image imgPagePeel = new Bitmap(WorkingDirectory + "\\PagePeel.bmp");
int ppWidth = imgPagePeel.Width;
int ppHeight = imgPagePeel.Height;
//Set the rendering quality for this Graphics object
grPhoto.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
//Draws the photo Image object at original size to the graphics object.
grPhoto.DrawImage(
imgPhoto, // Photo Image object
new Rectangle(0, 0, phWidth, phHeight), // Rectangle structure
0, // x-coordinate of the portion of the source image to draw.
0, // y-coordinate of the portion of the source image to draw.
phWidth, // Width of the portion of the source image to draw.
phHeight, // Height of the portion of the source image to draw.
GraphicsUnit.Pixel); // Units of measure
//The first step in manipulating the PagePeel image is to replace
//the background color with one that is transparent (Alpha=0, R=0, G=0, B=0)
//to do this we will use a Colormap and define ImageAttributes with a RemapTable
ImageAttributes imageAttributes = new ImageAttributes();
ColorMap colorMap = new ColorMap();
//My PagePeel was defined with a background of 100% Green this will
//be the color we search for and replace with transparency
colorMap.OldColor = Color.FromArgb(255, 0, 255, 0);
colorMap.NewColor = Color.FromArgb(0, 0, 0, 0);
//Set the Remap Table with the old and new color map
ColorMap[] remapTable = { colorMap };
imageAttributes.SetRemapTable(remapTable, ColorAdjustType.Bitmap);
//For this example we will place the PagePeel in the bottom right
//hand corner of the photograph
int xPosOfPp = phWidth - ppWidth;
int yPosOfPp = phHeight - ppHeight + 1;
grPhoto.DrawImage(imgPagePeel,
new Rectangle(xPosOfPp, yPosOfPp, ppWidth, ppHeight), //Set the detination Position
0, // x-coordinate of the portion of the source image to draw.
0, // y-coordinate of the portion of the source image to draw.
ppWidth, // PagePeel Width
ppHeight, // PagePeel Height
GraphicsUnit.Pixel, // Unit of measurment
imageAttributes); //ImageAttributes Object
//Replace the original photgraphs bitmap with the new Bitmap
imgPhoto = bmPhoto;
grPhoto.Dispose();
//save new image to file system.
imgPhoto.Save(WorkingDirectory + "\\after.jpg", ImageFormat.Jpeg);
imgPhoto.Dispose();
imgPagePeel.Dispose();
//Show the After image
picAfter.Image = Image.FromFile(WorkingDirectory + "\\after.jpg");
}
The PagePeel.bmp:
The Before and After result:
Update
Here's a version that uses a Transparent Page Peel overlay so you dont neet to convert the "green screen" to invisible. The advantage of this method is when the original photograph contains the colour green RGB(0,255,0) it wont be turned into transparent:
TransparentPagePeel.png:
private void button2_Click(object sender, EventArgs e)
{
//create a image object containing the photograph to add page peel effect
Image imgPhoto = Image.FromFile(WorkingDirectory + "\\before.jpg");
int phWidth = imgPhoto.Width;
int phHeight = imgPhoto.Height;
//create a Bitmap the Size of the original photograph
Bitmap bmPhoto = new Bitmap(phWidth, phHeight, PixelFormat.Format24bppRgb);
bmPhoto.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution);
//load the Bitmap into a Graphics object
Graphics grPhoto = Graphics.FromImage(bmPhoto);
//create a image object containing the PagePeel
Image imgPagePeel = new Bitmap(WorkingDirectory + "\\transparentPagePeel.png");
int ppWidth = imgPagePeel.Width;
int ppHeight = imgPagePeel.Height;
//Set the rendering quality for this Graphics object
grPhoto.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
//Draws the photo Image object at original size to the graphics object.
grPhoto.DrawImage(
imgPhoto, // Photo Image object
new Rectangle(0, 0, phWidth, phHeight), // Rectangle structure
0, // x-coordinate of the portion of the source image to draw.
0, // y-coordinate of the portion of the source image to draw.
phWidth, // Width of the portion of the source image to draw.
phHeight, // Height of the portion of the source image to draw.
GraphicsUnit.Pixel); // Units of measure
//For this example we will place the PagePeel in the bottom right
//hand corner of the photograph
int xPosOfPp = phWidth - ppWidth;
int yPosOfPp = phHeight - ppHeight + 1;
grPhoto.DrawImage(imgPagePeel,
new Rectangle(xPosOfPp, yPosOfPp, ppWidth, ppHeight), //Set the detination Position
0, // x-coordinate of the portion of the source image to draw.
0, // y-coordinate of the portion of the source image to draw.
ppWidth, // PagePeel Width
ppHeight, // PagePeel Height
GraphicsUnit.Pixel, // Unit of measurment
null); //ImageAttributes Object
//Replace the original photgraphs bitmap with the new Bitmap
imgPhoto = bmPhoto;
grPhoto.Dispose();
//save new image to file system.
imgPhoto.Save(WorkingDirectory + "\\after1.jpg", ImageFormat.Jpeg);
imgPhoto.Dispose();
imgPagePeel.Dispose();
picAfter.Image = Image.FromFile(WorkingDirectory + "\\after1.jpg");
}

Size of image increases after cropping

I have c# code to crop images.
When i crop image (size:191 KB, using my c# code) then size of resulted (cropped) image increases (size:2.44 MB)
Please tell me why size increases after cropping ..???
Bitmap source = new Bitmap(#"F:\images\Row" + i + "Col" + j + ".jpg");
Rectangle section = new Rectangle(new Point(0, 0), new Size(1362, 761));
Bitmap CroppedImage = CropImage(source, section);
CroppedImage.Save(#"file path\Row" + i + "Col" + j + ".jpg");
public Bitmap CropImage(Bitmap source, Rectangle section)
{
// An empty bitmap which will hold the cropped image
Bitmap bmp = new Bitmap(section.Width, section.Height);
Graphics g = Graphics.FromImage(bmp);
// Draw the given area (section) of the source image
// at location 0,0 on the empty bitmap (bmp)
g.DrawImage(source, 0, 0, section, GraphicsUnit.Pixel);
return bmp;
}
Telepathic power: you are talking about size of file on disk and comparing original compressed file (likely JPG) with cropped version saved in non-compressed format (likely BMP).
Fix: save cropped image in compressed format.
Image.Save with 2 arguments lets you specify format (i.e. unlike one argument version you use in your sample).
Example from the article:
// Construct a bitmap from the button image resource.
Bitmap bmp1 = new Bitmap(typeof(Button), "Button.bmp");
// Save the image as a GIF.
bmp1.Save("c:\\button.gif", System.Drawing.Imaging.ImageFormat.Gif);

How can I create thumbnail from WPF window and convert it into bytes[] so I can persist it?

I would like to create Thumbnail from my wpf window and would like to save it into database and display it later. Is there any good solution?
I have started with RenderTargetBitmap but I can't find any easy way to get it into bytes.
RenderTargetBitmap bmp = new RenderTargetBitmap(180, 180, 96, 96, PixelFormats.Pbgra32);
bmp.Render(myWpfWindow);
Using user32.dll and Graphics.CopyFromScreen() is not good for me and
as it is here because I want to do a screenshot from user controls as well.
Thanks
Steven Robbins has written a great blog post about capturing a screenshot of a control, which contains the following extension method:
public static class Screenshot
{
/// <summary>
/// Gets a JPG "screenshot" of the current UIElement
/// </summary>
/// <param name="source">UIElement to screenshot</param>
/// <param name="scale">Scale to render the screenshot</param>
/// <param name="quality">JPG Quality</param>
/// <returns>Byte array of JPG data</returns>
public static byte[] GetJpgImage(this UIElement source, double scale, int quality)
{
double actualHeight = source.RenderSize.Height;
double actualWidth = source.RenderSize.Width;
double renderHeight = actualHeight * scale;
double renderWidth = actualWidth * scale;
RenderTargetBitmap renderTarget = new RenderTargetBitmap((int) renderWidth, (int) renderHeight, 96, 96, PixelFormats.Pbgra32);
VisualBrush sourceBrush = new VisualBrush(source);
DrawingVisual drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.RenderOpen();
using (drawingContext)
{
drawingContext.PushTransform(new ScaleTransform(scale, scale));
drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(0, 0), new Point(actualWidth, actualHeight)));
}
renderTarget.Render(drawingVisual);
JpegBitmapEncoder jpgEncoder = new JpegBitmapEncoder();
jpgEncoder.QualityLevel = quality;
jpgEncoder.Frames.Add(BitmapFrame.Create(renderTarget));
Byte[] _imageArray;
using (MemoryStream outputStream = new MemoryStream())
{
jpgEncoder.Save(outputStream);
_imageArray = outputStream.ToArray();
}
return _imageArray;
}
}
This method takes a control and a scale factor and returns a byte array. So this seems to be a quite good fit with your requirements.
Check out the post for further reading and an example project that is quite neat.
You can use a BitmapEncoder to encode your Bitmap to a PNG, JPG or even BMP file. Check the MSDN documentation on BitmapEncoder.Frames, which has an example that saves to a FileStream. You can save it to any stream.
To get the BitmapFrame from the RenderTargetBitmap, simply create it using the BitmapFrame.Create(BitmapSource) method.

Categories

Resources