how do I extract Images, which are FlateDecoded (such like PNG) out of a PDF-Document with PDFSharp?
I found that comment in a Sample of PDFSharp:
// TODO: You can put the code here that converts vom PDF internal image format to a
// Windows bitmap
// and use GDI+ to save it in PNG format.
// [...]
// Take a look at the file
// PdfSharp.Pdf.Advanced/PdfImage.cs to see how we create the PDF image formats.
Does anyone have a solution for this problem?
Thanks for your replies.
EDIT: Because I'm not able to answer on my own Question within 8 hours, I do it on that way:
Thanks for your very fast reply.
I added some Code to the Method "ExportAsPngImage", but I didn't get the wanted results. It is just extracting a few more Images (png) and they don't have the right colors and are distorted.
Here's my actual Code:
PdfSharp.Pdf.Filters.FlateDecode flate = new PdfSharp.Pdf.Filters.FlateDecode();
byte[] decodedBytes = flate.Decode(bytes);
System.Drawing.Imaging.PixelFormat pixelFormat;
switch (bitsPerComponent)
{
case 1:
pixelFormat = PixelFormat.Format1bppIndexed;
break;
case 8:
pixelFormat = PixelFormat.Format8bppIndexed;
break;
case 24:
pixelFormat = PixelFormat.Format24bppRgb;
break;
default:
throw new Exception("Unknown pixel format " + bitsPerComponent);
}
Bitmap bmp = new Bitmap(width, height, pixelFormat);
var bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, pixelFormat);
int length = (int)Math.Ceiling(width * bitsPerComponent / 8.0);
for (int i = 0; i < height; i++)
{
int offset = i * length;
int scanOffset = i * bmpData.Stride;
Marshal.Copy(decodedBytes, offset, new IntPtr(bmpData.Scan0.ToInt32() + scanOffset), length);
}
bmp.UnlockBits(bmpData);
using (FileStream fs = new FileStream(#"C:\Export\PdfSharp\" + String.Format("Image{0}.png", count), FileMode.Create, FileAccess.Write))
{
bmp.Save(fs, System.Drawing.Imaging.ImageFormat.Png);
}
Is that the right way? Or should I choose another way? Thanks a lot!
I know this answer might be a few years to late, but maybe it will help others.
The disortion occurs in my case because image.Elements.GetInteger(PdfImage.Keys.BitsPerComponent) seems to not return the correct value. As Vive la déraison pointed out under your question, you get the BGR Format for using Marshal.Copy. So reversing the Bytes and rotating the Bitmap after executing Marshal.Copy will do the job.
The resulting code looks like this:
private static void ExportAsPngImage(PdfDictionary image, ref int count)
{
int width = image.Elements.GetInteger(PdfImage.Keys.Width);
int height = image.Elements.GetInteger(PdfImage.Keys.Height);
var canUnfilter = image.Stream.TryUnfilter();
byte[] decodedBytes;
if (canUnfilter)
{
decodedBytes = image.Stream.Value;
}
else
{
PdfSharp.Pdf.Filters.FlateDecode flate = new PdfSharp.Pdf.Filters.FlateDecode();
decodedBytes = flate.Decode(image.Stream.Value);
}
int bitsPerComponent = 0;
while (decodedBytes.Length - ((width * height) * bitsPerComponent / 8) != 0)
{
bitsPerComponent++;
}
System.Drawing.Imaging.PixelFormat pixelFormat;
switch (bitsPerComponent)
{
case 1:
pixelFormat = System.Drawing.Imaging.PixelFormat.Format1bppIndexed;
break;
case 8:
pixelFormat = System.Drawing.Imaging.PixelFormat.Format8bppIndexed;
break;
case 16:
pixelFormat = System.Drawing.Imaging.PixelFormat.Format16bppArgb1555;
break;
case 24:
pixelFormat = System.Drawing.Imaging.PixelFormat.Format24bppRgb;
break;
case 32:
pixelFormat = System.Drawing.Imaging.PixelFormat.Format32bppArgb;
break;
case 64:
pixelFormat = System.Drawing.Imaging.PixelFormat.Format64bppArgb;
break;
default:
throw new Exception("Unknown pixel format " + bitsPerComponent);
}
decodedBytes = decodedBytes.Reverse().ToArray();
Bitmap bmp = new Bitmap(width, height, pixelFormat);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
int length = (int)Math.Ceiling(width * (bitsPerComponent / 8.0));
for (int i = 0; i < height; i++)
{
int offset = i * length;
int scanOffset = i * bmpData.Stride;
Marshal.Copy(decodedBytes, offset, new IntPtr(bmpData.Scan0.ToInt32() + scanOffset), length);
}
bmp.UnlockBits(bmpData);
bmp.RotateFlip(RotateFlipType.Rotate180FlipNone);
bmp.Save(String.Format("exported_Images\\Image{0}.png", count++), System.Drawing.Imaging.ImageFormat.Png);
}
The code might need some optimisation, but it did export FlateDecoded Images correctly in my case.
To get a Windows BMP, you just have to create a Bitmap header and then copy the image data into the bitmap. PDF images are byte aligned (every new line starts on a byte boundary) while Windows BMPs are DWORD aligned (every new line starts on a DWORD boundary (a DWORD is 4 bytes for historical reasons)).
All information you need for the Bitmap header can be found in the filter parameters or can be calculated.
The color palette is another FlateEncoded object in the PDF. You also copy that into the BMP.
This must be done for several formats (1 bit per pixel, 8 bpp, 24 bpp, 32 bpp).
Here's my full code for doing this.
I'm extracting a UPS shipping label from a PDF so I know the format in advance. If your extracted image is of an unknown type then you'll need to check the bitsPerComponent and handle it accordingly. I also only handle the first image here on the first page.
Note: I'm using TryUnfilter to 'deflate' which uses whatever filter is applied and decodes the data in-place for me. No need to call 'Deflate' explicitly.
var file = #"c:\temp\PackageLabels.pdf";
var doc = PdfReader.Open(file);
var page = doc.Pages[0];
{
// Get resources dictionary
PdfDictionary resources = page.Elements.GetDictionary("/Resources");
if (resources != null)
{
// Get external objects dictionary
PdfDictionary xObjects = resources.Elements.GetDictionary("/XObject");
if (xObjects != null)
{
ICollection<PdfItem> items = xObjects.Elements.Values;
// Iterate references to external objects
foreach (PdfItem item in items)
{
PdfReference reference = item as PdfReference;
if (reference != null)
{
PdfDictionary xObject = reference.Value as PdfDictionary;
// Is external object an image?
if (xObject != null && xObject.Elements.GetString("/Subtype") == "/Image")
{
// do something with your image here
// only the first image is handled here
var bitmap = ExportImage(xObject);
bmp.Save(#"c:\temp\exported.png", System.Drawing.Imaging.ImageFormat.Bmp);
}
}
}
}
}
}
Using these helper functions
private static Bitmap ExportImage(PdfDictionary image)
{
string filter = image.Elements.GetName("/Filter");
switch (filter)
{
case "/FlateDecode":
return ExportAsPngImage(image);
default:
throw new ApplicationException(filter + " filter not implemented");
}
}
private static Bitmap ExportAsPngImage(PdfDictionary image)
{
int width = image.Elements.GetInteger(PdfImage.Keys.Width);
int height = image.Elements.GetInteger(PdfImage.Keys.Height);
int bitsPerComponent = image.Elements.GetInteger(PdfImage.Keys.BitsPerComponent);
var canUnfilter = image.Stream.TryUnfilter();
var decoded = image.Stream.Value;
Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
Marshal.Copy(decoded, 0, bmpData.Scan0, decoded.Length);
bmp.UnlockBits(bmpData);
return bmp;
}
So far... my code... it works with many png files, but not the one that comes from adobe photoshop with colorspace indexed:
private bool ExportAsPngImage(PdfDictionary image, string SaveAsName)
{
int width = image.Elements.GetInteger(PdfSharp.Pdf.Advanced.PdfImage.Keys.Width);
int height = image.Elements.GetInteger(PdfSharp.Pdf.Advanced.PdfImage.Keys.Height);
int bitsPerComponent = image.Elements.GetInteger(PdfSharp.Pdf.Advanced.PdfImage.Keys.BitsPerComponent);
var ColorSpace = image.Elements.GetArray(PdfImage.Keys.ColorSpace);
System.Drawing.Imaging.PixelFormat pixelFormat= System.Drawing.Imaging.PixelFormat.Format24bppRgb; //24 just for initalize
if (ColorSpace is null) //no colorspace.. bufferedimage?? is in BGR order instead of RGB so change the byte order. Right now it works
{
byte[] origineel_byte_boundary = image.Stream.UnfilteredValue;
bitsPerComponent = (origineel_byte_boundary.Length) / (width * height);
switch (bitsPerComponent)
{
case 4:
pixelFormat = System.Drawing.Imaging.PixelFormat.Format32bppPArgb;
break;
case 3:
pixelFormat = System.Drawing.Imaging.PixelFormat.Format24bppRgb;
break;
default:
{
MessageBox.Show("Unknown pixel format " + bitsPerComponent, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return false;
}
break;
}
Bitmap bmp = new Bitmap(width, height, pixelFormat); //copy raw bytes to "master" bitmap so we are out of pdf format to work with
System.Drawing.Imaging.BitmapData bmd = bmp.LockBits(new Rectangle(0, 0, width, height), System.Drawing.Imaging.ImageLockMode.WriteOnly, pixelFormat);
System.Runtime.InteropServices.Marshal.Copy(origineel_byte_boundary, 0, bmd.Scan0, origineel_byte_boundary.Length);
bmp.UnlockBits(bmd);
Bitmap bmp2 = new Bitmap(width, height, pixelFormat);
for (int indicex = 0; indicex < bmp.Width; indicex++)
{
for (int indicey = 0; indicey < bmp.Height; indicey++)
{
Color nuevocolor = bmp.GetPixel(indicex, indicey);
Color colorintercambiado = Color.FromArgb(nuevocolor.A, nuevocolor.B, nuevocolor.G, nuevocolor.R);
bmp2.SetPixel(indicex, indicey, colorintercambiado);
}
}
using (FileStream fs = new FileStream(SaveAsName, FileMode.Create, FileAccess.Write))
{
bmp2.Save(fs, System.Drawing.Imaging.ImageFormat.Png);
}
bmp2.Dispose();
bmp.Dispose();
}
else
{
// this is the case of photoshop... work needs to be done here. I ´m able to get the color palette but no idea how to put it back or create the png file...
switch (bitsPerComponent)
{
case 4:
pixelFormat = System.Drawing.Imaging.PixelFormat.Format32bppArgb;
break;
default:
{
MessageBox.Show("Unknown pixel format " + bitsPerComponent, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return false;
}
break;
}
if ((ColorSpace.Elements.GetName(0) == "/Indexed") && (ColorSpace.Elements.GetName(1) == "/DeviceRGB"))
{
//we need to create the palette
int paletteColorCount = ColorSpace.Elements.GetInteger(2);
List<System.Drawing.Color> paletteList = new List<Color>();
//Color[] palette = new Color[paletteColorCount+1]; // no idea why but it seams that there´s always 1 color more. ¿transparency?
PdfObject paletteObj = ColorSpace.Elements.GetObject(3);
PdfDictionary paletteReference = (PdfDictionary)paletteObj;
byte[] palettevalues = paletteReference.Stream.Value;
for (int index = 0; index < (paletteColorCount + 1); index++)
{
//palette[index] = Color.FromArgb(1, palettevalues[(index*3)], palettevalues[(index*3)+1], palettevalues[(index*3)+2]); // RGB
paletteList.Add(Color.FromArgb(1, palettevalues[(index * 3)], palettevalues[(index * 3) + 1], palettevalues[(index * 3) + 2])); // RGB
}
}
}
return true;
}
PDF may contain images with masks and with different colorspace options that is why simply decoding an image object may not work properly in some cases.
So the code also needs to check for image masks (/ImageMask) and other properties of image objects (to see if image should also use inverted colors or uses indexed colors) inside PDF to recreate the image similar to how it is displayed in PDF. See Image object, /ImageMask and /Decode dictionaries in the official PDF Reference.
Not sure if PDFSharp is capable of finding Image Mask objects inside PDF but iTextSharp is able to access image mask objects (see PdfName.MASK object types).
Commercial tools like PDF Extractor SDK are able to extract images in both original form and in "as rendered" form.
I work for ByteScout, maker of PDF Extractor SDK
Maybe not directly answer the question but another option to extract images from PDF is to use FreeSpire.PDF which can extract the image from pdf easily. It is available as Nuget package https://www.nuget.org/packages/FreeSpire.PDF/. They handle all the image format and can export as PNG. Their sample code is
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using Spire.Pdf;
namespace ExtractImagesFromPDF
{
class Program
{
static void Main(string[] args)
{
//Instantiate an object of Spire.Pdf.PdfDocument
PdfDocument doc = new PdfDocument();
//Load a PDF file
doc.LoadFromFile("sample.pdf");
List<Image> ListImage = new List<Image>();
for (int i = 0; i < doc.Pages.Count; i++)
{
// Get an object of Spire.Pdf.PdfPageBase
PdfPageBase page = doc.Pages[i];
// Extract images from Spire.Pdf.PdfPageBase
Image[] images = page.ExtractImages();
if (images != null && images.Length > 0)
{
ListImage.AddRange(images);
}
}
if (ListImage.Count > 0)
{
for (int i = 0; i < ListImage.Count; i++)
{
Image image = ListImage[i];
image.Save("image" + (i + 1).ToString() + ".png", System.Drawing.Imaging.ImageFormat.Png);
}
System.Diagnostics.Process.Start("image1.png");
}
}
}
}
(code taken from https://www.e-iceblue.com/Tutorials/Spire.PDF/Spire.PDF-Program-Guide/How-to-Extract-Image-From-PDF-in-C.html)
Related
I want to export some image from some PDF File, to do this I should to use a PdfSharp library.
I have find the code on the web to export image file from one PDF but if the image is codifing by DCTDecode I don't have any problems. If the image ise codifing by FlatDecode mode I'm not able to export this Image.
So this is the code:
static void Main(string[] args)
{
//estrapolare immagine da pdf
const string filename = "d://eresult.pdf";
PdfDocument document = PdfReader.Open(filename);
int imageCount = 0;
// Iterate pages
foreach (PdfPage page in document.Pages)
{
// Get resources dictionary
PdfDictionary resources = page.Elements.GetDictionary("/Resources");
if (resources != null)
{
// Get external objects dictionary
PdfDictionary xObjects = resources.Elements.GetDictionary("/XObject");
if (xObjects != null)
{
ICollection<PdfItem> items = xObjects.Elements.Values;
// Iterate references to external objects
foreach (PdfItem item in items)
{
PdfReference reference = item as PdfReference;
if (reference != null)
{
PdfDictionary xObject = reference.Value as PdfDictionary;
// Is external object an image?
if (xObject != null && xObject.Elements.GetString("/Subtype") == "/Image")
{
ExportImage(xObject, ref imageCount);
}
}
}
}
}
}
System.Diagnostics.Debug.Write(imageCount + " images exported.", "Export Images");
}
static void ExportImage(PdfDictionary image, ref int count)
{
string filter = image.Elements.GetName("/Filter");
switch (filter)
{
case "/DCTDecode":
ExportJpegImage(image, ref count);
break;
case "/FlateDecode":
ExportAsPngImage(image, ref count);
break;
}
}
static void ExportJpegImage(PdfDictionary image, ref int count)
{
// Fortunately JPEG has native support in PDF and exporting an image is just writing the stream to a file.
byte[] stream = image.Stream.Value;
FileStream fs = new FileStream(String.Format("Image{0}.jpeg", count++), FileMode.Create, FileAccess.Write);
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(stream);
bw.Close();
}
static void ExportAsPngImage(PdfDictionary image, ref int count)
{
int width = image.Elements.GetInteger(PdfImage.Keys.Width);
int height = image.Elements.GetInteger(PdfImage.Keys.Height);
int bitsPerComponent = image.Elements.GetInteger(PdfImage.Keys.BitsPerComponent);
PdfSharp.Pdf.Filters.FlateDecode flate = new PdfSharp.Pdf.Filters.FlateDecode();
byte[] decodedBytes = flate.Decode(image.Stream.Value);
System.Drawing.Imaging.PixelFormat pixelFormat;
switch (bitsPerComponent)
{
case 1:
pixelFormat = PixelFormat.Format1bppIndexed;
break;
case 8:
pixelFormat = PixelFormat.Format8bppIndexed;
break;
case 24:
pixelFormat = PixelFormat.Format24bppRgb;
break;
default:
throw new Exception("Unknown pixel format " + bitsPerComponent);
}
Bitmap bmp = new Bitmap(width, height, pixelFormat);
var bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, pixelFormat);
int length = (int)Math.Ceiling(width * bitsPerComponent / 8.0);
for (int i = 0; i < height; i++)
{
int offset = i * length;
int scanOffset = i * bmpData.Stride;
Marshal.Copy(decodedBytes, offset, new IntPtr(bmpData.Scan0.ToInt32() + scanOffset), length);
}
bmp.UnlockBits(bmpData);
using (FileStream fs = new FileStream(#"D:\\" + String.Format("Image{0}.png", count++), FileMode.Create, FileAccess.Write))
{
bmp.Save(fs, System.Drawing.Imaging.ImageFormat.Png);
}
// TODO: You can put the code here that converts vom PDF internal image format to a Windows bitmap
// and use GDI+ to save it in PNG format.
// It is the work of a day or two for the most important formats. Take a look at the file
// PdfSharp.Pdf.Advanced/PdfImage.cs to see how we create the PDF image formats.
// We don't need that feature at the moment and therefore will not implement it.
// If you write the code for exporting images I would be pleased to publish it in a future release
// of PDFsharp.
}
With this code I can see the Image in this strange mode:
But the image in the Pdf file is this:
As you can see, the color is too different
Image data and color palette are different objects in the PDF file. Images can have masks and these would be different objects, too.
When saving image data to a PNG file, you also may have to get the color palette and include the color data in the PNG file.
Maybe the code shown on the PDFsharp forum works better than your code:
http://forum.pdfsharp.net/viewtopic.php?p=6755#p6755
I want to read a dicom image using simpleitk, convert it into a bitmap and then display the result in a pictureBox. But when I'm trying to do this, an ArgumentException is thrown. How can I solve this?
Here is my code:
OpenFileDialog dialog = new OpenFileDialog();
dialog.Title = "Open";
dialog.Filter = "DICOM Files (*.dcm;*.dic)|*.dcm;*.dic|All Files (*.*)|*.*";
dialog.ShowDialog();
if (dialog.FileName != "")
{
using (sitk.ImageFileReader reader = new sitk.ImageFileReader())
{
reader.SetFileName(dialog.FileName);
reader.SetOutputPixelType(sitk.PixelIDValueEnum.sitkFloat32);
sitk.Image image = reader.Execute();
var castedImage = sitk.SimpleITK.Cast(image,
sitk.PixelIDValueEnum.sitkFloat32);
var size = castedImage.GetSize();
int length = size.Aggregate(1, (current, i) => current * (int)i);
IntPtr buffer = castedImage.GetBufferAsFloat();
// Declare an array to hold the bytes of the bitmap.
byte[] rgbValues = new byte[length];
// Copy the RGB values into the array.
Marshal.Copy(buffer, rgbValues, 0, length);
Stream stream = new MemoryStream(rgbValues);
Bitmap newBitmap = new Bitmap(stream);
//I have tried in this way, but it generated ArgumentException too
//Bitmap newBitmap = new Bitmap((int)image.GetWidth(), (int)image.GetHeight(), (int)image.GetDepth(), PixelFormat.Format8bppIndexed, buffer);
Obraz.pic.Image = newBitmap;
}
}
Thank you for your comments and attempts to help. After consultations and my own searching on the internet I solved this issue. The first problem was the inadequate representation of pixel image. I had to change Float32 to UInt8 to provide an eight-bit for pixel.
var castedImage = sitk.SimpleITK.Cast(image2, sitk.PixelIDValueEnum.sitkUInt8);
Then I would already create a Bitmap using the constructor that was was commented out in question, but with (int)image.GetWidth() instead of (int)image.GetDepth().
Bitmap newBitmap = new Bitmap((int)image.GetWidth(), (int)image.GetHeight(), (int)image.GetWidth(), PixelFormat.Format8bppIndexed, buffer);
Unfortunately, a new problem appeared. The image, that was supposed to be in gray scale, was displayed in strange colors. But I found the solution here
ColorPalette pal = newBitmap.Palette;
for (int i = 0; i <= 255; i++)
{
// create greyscale color table
pal.Entries[i] = Color.FromArgb(i, i, i);
}
newBitmap.Palette = pal; // you need to re-set this property to force the new ColorPalette
I'm saving a bitmap to a file on my hard drive inside of a loop (All the jpeg files within a directory are being saved to a database). The save works fine the first pass through the loop, but then gives the subject error on the second pass. I thought perhaps the file was getting locked so I tried generating a unique file name for each pass, and I'm also using Dispose() on the bitmap after the file get saved. Any idea what is causing this error?
Here is my code:
private string fileReducedDimName = #"c:\temp\Photos\test\filePhotoRedDim";
...
foreach (string file in files)
{
int i = 0;
//if the file dimensions are big, scale the file down
Stream photoStream = File.OpenRead(file);
byte[] photoByte = new byte[photoStream.Length];
photoStream.Read(photoByte, 0, System.Convert.ToInt32(photoByte.Length));
Image image = Image.FromStream(new MemoryStream(photoByte));
Bitmap bm = ScaleImage(image);
bm.Save(fileReducedDimName + i.ToString() + ".jpg", ImageFormat.Jpeg);//error occurs here
Array.Clear(photoByte,0, photoByte.Length);
bm.Dispose();
i ++;
}
...
Thanks
Here's the scale image code: (this seems to be working ok)
protected Bitmap ScaleImage(System.Drawing.Image Image)
{
//reduce dimensions of image if appropriate
int destWidth;
int destHeight;
int sourceRes;//resolution of image
int maxDimPix;//largest dimension of image pixels
int maxDimInch;//largest dimension of image inches
Double redFactor;//factor to reduce dimensions by
if (Image.Width > Image.Height)
{
maxDimPix = Image.Width;
}
else
{
maxDimPix = Image.Height;
}
sourceRes = Convert.ToInt32(Image.HorizontalResolution);
maxDimInch = Convert.ToInt32(maxDimPix / sourceRes);
//Assign size red factor based on max dimension of image (inches)
if (maxDimInch >= 17)
{
redFactor = 0.45;
}
else if (maxDimInch < 17 && maxDimInch >= 11)
{
redFactor = 0.65;
}
else if (maxDimInch < 11 && maxDimInch >= 8)
{
redFactor = 0.85;
}
else//smaller than 8" dont reduce dimensions
{
redFactor = 1;
}
destWidth = Convert.ToInt32(Image.Width * redFactor);
destHeight = Convert.ToInt32(Image.Height * redFactor);
Bitmap bm = new Bitmap(destWidth, destHeight,
PixelFormat.Format24bppRgb);
bm.SetResolution(Image.HorizontalResolution, Image.VerticalResolution);
Graphics grPhoto = Graphics.FromImage(bm);
grPhoto.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
grPhoto.DrawImage(Image,
new Rectangle(0, 0, destWidth, destHeight),
new Rectangle(0, 0, Image.Width, Image.Height),
GraphicsUnit.Pixel);
grPhoto.Dispose();
return bm;
}
If I'm reading the code right, your i variable is zero every time through the loop.
It is hard to diagnose exactly what is wrong, I would recommend that you use using statements to ensure that your instances are getting disposed of properly, but it looks like they are.
I originally thought it might be an issue with the ScaleImage. So I tried a different resize function (C# GDI+ Image Resize Function) and it worked, but i is always set to zero at beginning of each loop. Once you move i's initialization outside of the loop your scale method works as well.
private void MethodName()
{
string fileReducedDimName = #"c:\pics";
int i = 0;
foreach (string file in Directory.GetFiles(fileReducedDimName, "*.jpg"))
{
//if the file dimensions are big, scale the file down
using (Image image = Image.FromFile(file))
{
using (Bitmap bm = ScaleImage(image))
{
bm.Save(fileReducedDimName + #"\" + i.ToString() + ".jpg", ImageFormat.Jpeg);//error occurs here
//this is all redundant code - do not need
//Array.Clear(photoByte, 0, photoByte.Length);
//bm.Dispose();
}
}
//ResizeImage(file, 50, 50, fileReducedDimName +#"\" + i.ToString()+".jpg");
i++;
}
}
I want to display thumbnail image in a gridview from file location. How to generate that of .jpeg file?
I am using C# language with asp.net.
You have to use GetThumbnailImage method in the Image class:
https://msdn.microsoft.com/en-us/library/8t23aykb%28v=vs.110%29.aspx
Here's a rough example that takes an image file and makes a thumbnail image from it, then saves it back to disk.
Image image = Image.FromFile(fileName);
Image thumb = image.GetThumbnailImage(120, 120, ()=>false, IntPtr.Zero);
thumb.Save(Path.ChangeExtension(fileName, "thumb"));
It is in the System.Drawing namespace (in System.Drawing.dll).
Behavior:
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.
Important: the remarks section of the Microsoft link above warns of certain potential problems:
The GetThumbnailImage method works well when the requested thumbnail
image has a size of about 120 x 120 pixels. If you request a large
thumbnail image (for example, 300 x 300) from an Image that has an
embedded thumbnail, there could be a noticeable loss of quality in the
thumbnail image.
It might be better to scale the main image (instead
of scaling the embedded thumbnail) by calling the DrawImage method.
The following code will write an image in proportional to the response, you can modify the code for your purpose:
public void WriteImage(string path, int width, int height)
{
Bitmap srcBmp = new Bitmap(path);
float ratio = srcBmp.Width / srcBmp.Height;
SizeF newSize = new SizeF(width, height * ratio);
Bitmap target = new Bitmap((int) newSize.Width,(int) newSize.Height);
HttpContext.Response.Clear();
HttpContext.Response.ContentType = "image/jpeg";
using (Graphics graphics = Graphics.FromImage(target))
{
graphics.CompositingQuality = CompositingQuality.HighSpeed;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.DrawImage(srcBmp, 0, 0, newSize.Width, newSize.Height);
using (MemoryStream memoryStream = new MemoryStream())
{
target.Save(memoryStream, ImageFormat.Jpeg);
memoryStream.WriteTo(HttpContext.Response.OutputStream);
}
}
Response.End();
}
Here is a complete example of how to create a smaller image (thumbnail). This snippet resizes the Image, rotates it when needed (if a phone was held vertically) and pads the image if you want to create square thumbs. This snippet creates a JPEG, but it can easily be modified for other file types. Even if the image would be smaller than the max allowed size the image will still be compressed and it's resolution altered to create images of the same dpi and compression level.
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
//set the resolution, 72 is usually good enough for displaying images on monitors
float imageResolution = 72;
//set the compression level. higher compression = better quality = bigger images
long compressionLevel = 80L;
public Image resizeImage(Image image, int maxWidth, int maxHeight, bool padImage)
{
int newWidth;
int newHeight;
//first we check if the image needs rotating (eg phone held vertical when taking a picture for example)
foreach (var prop in image.PropertyItems)
{
if (prop.Id == 0x0112)
{
int orientationValue = image.GetPropertyItem(prop.Id).Value[0];
RotateFlipType rotateFlipType = getRotateFlipType(orientationValue);
image.RotateFlip(rotateFlipType);
break;
}
}
//apply the padding to make a square image
if (padImage == true)
{
image = applyPaddingToImage(image, Color.Red);
}
//check if the with or height of the image exceeds the maximum specified, if so calculate the new dimensions
if (image.Width > maxWidth || image.Height > maxHeight)
{
double ratioX = (double)maxWidth / image.Width;
double ratioY = (double)maxHeight / image.Height;
double ratio = Math.Min(ratioX, ratioY);
newWidth = (int)(image.Width * ratio);
newHeight = (int)(image.Height * ratio);
}
else
{
newWidth = image.Width;
newHeight = image.Height;
}
//start the resize with a new image
Bitmap newImage = new Bitmap(newWidth, newHeight);
//set the new resolution
newImage.SetResolution(imageResolution, imageResolution);
//start the resizing
using (var graphics = Graphics.FromImage(newImage))
{
//set some encoding specs
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawImage(image, 0, 0, newWidth, newHeight);
}
//save the image to a memorystream to apply the compression level
using (MemoryStream ms = new MemoryStream())
{
EncoderParameters encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, compressionLevel);
newImage.Save(ms, getEncoderInfo("image/jpeg"), encoderParameters);
//save the image as byte array here if you want the return type to be a Byte Array instead of Image
//byte[] imageAsByteArray = ms.ToArray();
}
//return the image
return newImage;
}
//=== image padding
public Image applyPaddingToImage(Image image, Color backColor)
{
//get the maximum size of the image dimensions
int maxSize = Math.Max(image.Height, image.Width);
Size squareSize = new Size(maxSize, maxSize);
//create a new square image
Bitmap squareImage = new Bitmap(squareSize.Width, squareSize.Height);
using (Graphics graphics = Graphics.FromImage(squareImage))
{
//fill the new square with a color
graphics.FillRectangle(new SolidBrush(backColor), 0, 0, squareSize.Width, squareSize.Height);
//put the original image on top of the new square
graphics.DrawImage(image, (squareSize.Width / 2) - (image.Width / 2), (squareSize.Height / 2) - (image.Height / 2), image.Width, image.Height);
}
//return the image
return squareImage;
}
//=== get encoder info
private ImageCodecInfo getEncoderInfo(string mimeType)
{
ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
for (int j = 0; j < encoders.Length; ++j)
{
if (encoders[j].MimeType.ToLower() == mimeType.ToLower())
{
return encoders[j];
}
}
return null;
}
//=== determine image rotation
private RotateFlipType getRotateFlipType(int rotateValue)
{
RotateFlipType flipType = RotateFlipType.RotateNoneFlipNone;
switch (rotateValue)
{
case 1:
flipType = RotateFlipType.RotateNoneFlipNone;
break;
case 2:
flipType = RotateFlipType.RotateNoneFlipX;
break;
case 3:
flipType = RotateFlipType.Rotate180FlipNone;
break;
case 4:
flipType = RotateFlipType.Rotate180FlipX;
break;
case 5:
flipType = RotateFlipType.Rotate90FlipX;
break;
case 6:
flipType = RotateFlipType.Rotate90FlipNone;
break;
case 7:
flipType = RotateFlipType.Rotate270FlipX;
break;
case 8:
flipType = RotateFlipType.Rotate270FlipNone;
break;
default:
flipType = RotateFlipType.RotateNoneFlipNone;
break;
}
return flipType;
}
//== convert image to base64
public string convertImageToBase64(Image image)
{
using (MemoryStream ms = new MemoryStream())
{
//convert the image to byte array
image.Save(ms, ImageFormat.Jpeg);
byte[] bin = ms.ToArray();
//convert byte array to base64 string
return Convert.ToBase64String(bin);
}
}
For the asp.net users a little example of how to upload a file, resize it and display the result on the page.
//== the button click method
protected void Button1_Click(object sender, EventArgs e)
{
//check if there is an actual file being uploaded
if (FileUpload1.HasFile == false)
{
return;
}
using (Bitmap bitmap = new Bitmap(FileUpload1.PostedFile.InputStream))
{
try
{
//start the resize
Image image = resizeImage(bitmap, 256, 256, true);
//to visualize the result, display as base64 image
Label1.Text = "<img src=\"data:image/jpg;base64," + convertImageToBase64(image) + "\">";
//save your image to file sytem, database etc here
}
catch (Exception ex)
{
Label1.Text = "Oops! There was an error when resizing the Image.<br>Error: " + ex.Message;
}
}
}
Here is a version based on the accepted answer. It fixes two problems...
Improper disposing of the images.
Maintaining the aspect ratio of the image.
I found this tool to be fast and effective for both JPG and PNG files.
private static FileInfo CreateThumbnailImage(string imageFileName, string thumbnailFileName)
{
const int thumbnailSize = 150;
using (var image = Image.FromFile(imageFileName))
{
var imageHeight = image.Height;
var imageWidth = image.Width;
if (imageHeight > imageWidth)
{
imageWidth = (int) (((float) imageWidth / (float) imageHeight) * thumbnailSize);
imageHeight = thumbnailSize;
}
else
{
imageHeight = (int) (((float) imageHeight / (float) imageWidth) * thumbnailSize);
imageWidth = thumbnailSize;
}
using (var thumb = image.GetThumbnailImage(imageWidth, imageHeight, () => false, IntPtr.Zero))
//Save off the new thumbnail
thumb.Save(thumbnailFileName);
}
return new FileInfo(thumbnailFileName);
}
Here is an example to convert high res image into thumbnail size-
protected void Button1_Click(object sender, EventArgs e)
{
//---------- Getting the Image File
System.Drawing.Image img = System.Drawing.Image.FromFile(Server.MapPath("~/profile/Avatar.jpg"));
//---------- Getting Size of Original Image
double imgHeight = img.Size.Height;
double imgWidth = img.Size.Width;
//---------- Getting Decreased Size
double x = imgWidth / 200;
int newWidth = Convert.ToInt32(imgWidth / x);
int newHeight = Convert.ToInt32(imgHeight / x);
//---------- Creating Small Image
System.Drawing.Image.GetThumbnailImageAbort myCallback = new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback);
System.Drawing.Image myThumbnail = img.GetThumbnailImage(newWidth, newHeight, myCallback, IntPtr.Zero);
//---------- Saving Image
myThumbnail.Save(Server.MapPath("~/profile/NewImage.jpg"));
}
public bool ThumbnailCallback()
{
return false;
}
Source-
http://iknowledgeboy.blogspot.in/2014/03/c-creating-thumbnail-of-large-image-by.html
This is the code I'm using. Also works for .NET Core > 2.0 using System.Drawing.Common NuGet.
https://www.nuget.org/packages/System.Drawing.Common/
using System;
using System.Drawing;
class Program
{
static void Main()
{
const string input = "C:\\background1.png";
const string output = "C:\\thumbnail.png";
// Load image.
Image image = Image.FromFile(input);
// Compute thumbnail size.
Size thumbnailSize = GetThumbnailSize(image);
// Get thumbnail.
Image thumbnail = image.GetThumbnailImage(thumbnailSize.Width,
thumbnailSize.Height, null, IntPtr.Zero);
// Save thumbnail.
thumbnail.Save(output);
}
static Size GetThumbnailSize(Image original)
{
// Maximum size of any dimension.
const int maxPixels = 40;
// Width and height.
int originalWidth = original.Width;
int originalHeight = original.Height;
// Return original size if image is smaller than maxPixels
if (originalWidth <= maxPixels || originalHeight <= maxPixels)
{
return new Size(originalWidth, originalHeight);
}
// Compute best factor to scale entire image based on larger dimension.
double factor;
if (originalWidth > originalHeight)
{
factor = (double)maxPixels / originalWidth;
}
else
{
factor = (double)maxPixels / originalHeight;
}
// Return thumbnail size.
return new Size((int)(originalWidth * factor), (int)(originalHeight * factor));
}
}
Source:
https://www.dotnetperls.com/getthumbnailimage
Sample Image:
I'm using DirectShow.net to get webcam footage into my program.
To accomplish this, I'm adding the source camera to the graph, and a VideoMixingRenderer9.
That part is all working swimmingly, but the part where I extract a frame using GetCurrentImage(out lpDib) is having what I can only describe as an odd issue.
What I am doing is using Marshal.PtrToSTructure to create a BitmapInfoHeader from lpDib, then calculating the width / height / stride / & pixel format.
The problem comes when I look at the image stored in bitmap - It has a 10 px wide line down the left side that came from what is actually the right!
It is worth noting that the data I am getting from the GetCurrentImage call is actually upside down - note the call to Cap.RotateFlip.
IntPtr lpDib;
windowlessCtrl.GetCurrentImage(out lpDib);
BitmapInfoHeader head;
head = (BitmapInfoHeader)Marshal.PtrToStructure(lpDib, typeof(BitmapInfoHeader));
int width = head.Width;
int height = head.Height;
int stride = width * (head.BitCount / 8);
PixelFormat pixelFormat = PixelFormat.Format24bppRgb;
switch (head.BitCount)
{
case 24: pixelFormat = PixelFormat.Format24bppRgb; break;
case 32: pixelFormat = PixelFormat.Format32bppRgb; break;
case 48: pixelFormat = PixelFormat.Format48bppRgb; break;
default: throw new Exception("Unknown BitCount");
}
Cap = new Bitmap(width, height, stride, pixelFormat, lpDib);
Cap.RotateFlip(RotateFlipType.RotateNoneFlipY);
//if we examine Cap here (Cap.Save, for example) I'm seeing the odd stripe.
I'm completely lost here. Seems like some sort of offset issue, and I've tried tweaking with stride some, but to no avail (just creates odd diagonal look).
This code is made using the DirectShowLib samples and it works:
public Bitmap GetCurrentImage()
{
Bitmap bmp = null;
if (windowlessCtrl != null)
{
IntPtr currentImage = IntPtr.Zero;
try
{
int hr = windowlessCtrl.GetCurrentImage(out currentImage);
DsError.ThrowExceptionForHR(hr);
if (currentImage != IntPtr.Zero)
{
BitmapInfoHeader structure = new BitmapInfoHeader();
Marshal.PtrToStructure(currentImage, structure);
PixelFormat pixelFormat = PixelFormat.Format24bppRgb;
switch (structure.BitCount)
{
case 24:
pixelFormat = PixelFormat.Format24bppRgb;
break;
case 32:
pixelFormat = PixelFormat.Format32bppRgb;
break;
case 48:
pixelFormat = PixelFormat.Format48bppRgb;
break;
default:
throw new Exception("BitCount desconhecido");
}
// este trecho: new IntPtr(currentImage.ToInt64() + 40), é o que resolve o problema da faixa (strip) da direita na esquerda.
bmp = new Bitmap(structure.Width, structure.Height, (structure.BitCount / 8) * structure.Width, pixelFormat, new IntPtr(currentImage.ToInt64() + 40));
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
}
}
catch (Exception anyException)
{
MessageBox.Show("Falha gravando imagem da Webcam: " + anyException.ToString());
}
finally
{
Marshal.FreeCoTaskMem(currentImage);
}
}
return bmp;
}
The video renderer is extending the bitmap to suit its own memory alignment needs, but it will have adjusted the media type to match. The VIDEOINFOHEADER (or VIDEOINFOHEADER2 structure) in the media type will have an rcTarget rectangle that defines the valid area within the larger bitmap. You can query the current media type on the input pin and get hold of this information.
You will find that the renderer only needs this extended stride for some formats, so perhaps your simplest approach is to force a different capture format. An alternative is to use the sample grabber filter instead of the VMR.
G
For those who want to avoid using SampleGrabber. The "stripe" issue can be fixed by adding the offset of the bitmap header to the IntPtr. However this requires unsafe code
IntPtr pBuffer = IntPtr.Zero;
int xBufferSize = 0;
int xWidth, xHeight;
basicVideo.get_VideoWidth(out xWidth);
basicVideo.get_VideoHeight(out xHeight);
int hr = basicVideo.GetCurrentImage(ref xBufferSize, IntPtr.Zero);
pBuffer = Marshal.AllocCoTaskMem(xBufferSize);
// Get the pixel buffer for the thumbnail
hr = basicVideo.GetCurrentImage(ref xBufferSize, pBuffer);
// Offset for BitmapHeader info
var bitmapHeader = (BitmapInfoHeader)Marshal.PtrToStructure(pBuffer, typeof(BitmapInfoHeader));
var pBitmapData = (byte*)pBuffer.ToPointer();
pBitmapData += bitmapHeader.Size;
// This will be the pointer to the bitmap pixels
var bitmapData = new IntPtr(pBitmapData);
//Change for your format type!
System.Drawing.Imaging.PixelFormat xFormat = (System.Drawing.Imaging.PixelFormat.Format32bppRgb);
int bitsPerPixel = ((int)xFormat & 0xff00) >> 8;
int bytesPerPixel = (bitsPerPixel + 7) / 8;
int stride = 4 * ((xWidth * bytesPerPixel + 3) / 4);
Bitmap image = new Bitmap(xWidth, xHeight, stride, xFormat, bitmapData);
image.RotateFlip(RotateFlipType.RotateNoneFlipY);
return image;
Calculation for Stride can be found here.