Converted Image to Gif, Works Partially? - c#

I've written a short method for converting images(Image class) to a GifEncoder and finally saving that Gif to a file. However, when I go to open the resulting Gif created, it has some problems. 1) The Gif stops playing after 2 cycles in a browser. 2) When loaded through an image editor the colors seem to mix up a bit between pixels.
public void ConvertToGif( string DestinationPath , Image myImage , int myFrames ) {
Bitmap myBitmap = new Bitmap( myImage.Width / myFrames , myImage.Height );
GifBitmapEncoder myEncoder = new GifBitmapEncoder();
int i = 0;
while( i < myFrames ) {
Graphics GrDrw = Graphics.FromImage( myBitmap );
var DestRegion = new Rectangle( 0 , 0 , myBitmap.Width , myBitmap.Height );
var SrceRegion = new Rectangle( myBitmap.Width * i , 0 , myBitmap.Width , myBitmap.Height );
GrDrw.DrawImage( myImage , DestRegion , SrceRegion , GraphicsUnit.Pixel );
BitmapSource mySource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap( myBitmap.GetHbitmap() , IntPtr.Zero , Int32Rect.Empty , BitmapSizeOptions.FromEmptyOptions() );
myEncoder.Frames.Add( BitmapFrame.Create( mySource , mySource ) );
GrDrw.Dispose();
i += 1;
}
System.IO.FileStream myStream = new System.IO.FileStream( #DestinationPath , System.IO.FileMode.Create );
myEncoder.Save( myStream );
}
The number of frames from an image created with ConvertToGif() also don't seem to register when running the resulting Gif through my other method, ConvertFromGif():
public Image ConvertFromGif( Image myImage ) {
var myDimensions = new FrameDimension( myImage.FrameDimensionsList[ 0 ] );
var myFrames = myImage.GetFrameCount( myDimensions );
var newImage = new Bitmap( myImage.Width * myFrames , myImage.Height );
for( int i = 0; i < myFrames; i ++ ) {
myImage.SelectActiveFrame( myDimensions , i );
var DestRegion = new Rectangle( myImage.Width * i , 0 , myImage.Width , myImage.Height );
var SrceRegion = new Rectangle( 0 , 0 , myImage.Width , myImage.Height );
Graphics GrDrw = Graphics.FromImage( newImage );
GrDrw.DrawImage( myImage , DestRegion , SrceRegion , GraphicsUnit.Pixel );
GrDrw.Dispose();
}
return newImage;
}
I can run any Gif through my ConvertFromGif() method, but Gifs made by my ConvertToGif() method won't work.

I had a Look around and discovered that GifBitmapEncoder is only really meant to read GIFs so they can be displayed in WPF this class will allow you to create multi-framed GIFs however it does not provide a Metadata handler that allows you to put key frame information like cycle count, delay between frame etc. They have left the Metadata part out for 3rd parties to implement
Unfortunately we don't expose a metadata reader or writer for GIFs
(animated or otherwise). In order to produce correct animated GIFs,
you need to write out some metadata with timing information. Since we
don't provide a GIF metadata handler, we do provide a mechanism for
3rd parties to write their own. Documentation for writing a metadata
handler can be found here:
http://msdn2.microsoft.com/en-us/library/ms737407.aspx as well as in
this article
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnlong/html/wiccodec.asp.
Quote from: http://social.msdn.microsoft.com/Forums/en-US/6ef358a7-d1ac-4267-91d9-166024aad8ca/creating-animated-gif-files-in-wpf?forum=wpf
Gifs have some default timing information so you are getting the 2 cycles but that is all it may also differ from other browsers etc.
Also while looking online I saw other users having the same issue with colour bleed between frames.
There is two avenues for you: Use a 3rd party .Net Library that allows you to write Gifs or write you own.
From your previous question Convert a List of Images to a GIf I would suggest you use NGif - http://www.codeproject.com/Articles/11505/NGif-Animated-GIF-Encoder-for-NET
However if you want to create your own metadata writer for the GifBitmapEncoder follow the links provided on the post above for the documentation for metadata read and writers.

Related

strategy of working with multiple image types and merging into 1 tiff

I don't have the slightest idea about graphics. I need to build a function that will take a list of different types of files (pngs, bmps, jpeg, pdf) and it will create 1 tiff file, merging all of the files together.
Can you guide me on the strategy in solving this? ( ...should i first convert every file to a tiff and then merge them together? ... should i simply use library XYZ to do this for me because this functionality already exists! should i first compress them somehow together and that will yield a tiff? )
It's doesn't matter what image format you want to use.You are working with bitmaps at the end. load all images and draw(merge) theme on single graphic object and at the end save the file as your own format :
static void Main(string[] args)
{
var imageList = Directory.GetFiles(#"C:\Users\ABC\Desktop\cars");
Bitmap destnationBitmap = new Bitmap(1000, 300);
Graphics g = Graphics.FromImage(destnationBitmap);
try
{
var drawPoint = new Point(0, 0);
foreach (string imagePath in imageList)
{
var tempBitmap = new Bitmap(imagePath);
g.DrawImage(tempBitmap, drawPoint);
drawPoint.X += tempBitmap.Width;
}
}
finally
{
var tiffVersion = ConverTo(destnationBitmap, ImageFormat.Tiff);
tiffVersion.Save("TiffVersion.tiff");
g.Dispose();
}
}
public static Image ConverTo(Bitmap bitmapImage, ImageFormat pFormat)
{
MemoryStream stream = new MemoryStream();
bitmapImage.Save(stream, pFormat);
return new Bitmap(stream);
}

failed to convert byte array into image in some certain browser

i am using Visual studio 2010 with .Net 3.5 framework
my code work fine in my windows 8, IE 9
however it doesnt work in windows xp , IE7, i have no idea what is exactly happen
Byte[] byImg = ((byte[])dr["logo"]);
var vBase64String = Convert.ToBase64String(byImg);
logo.ImageUrl = string.Format("data:image/gif;base64,{0}", vBase64String);
is there any support issue?
IE7 does not support data: protocol for images. It is implemented in IE8 and above.
If you need to support IE7 - server separate image files instead.
Try this:
string imageDataParsed = imageData.Substring( imageData.IndexOf( ',' ) + 1 );
byte[] imageBytes = Convert.FromBase64String( imageDataParsed );
using ( var imageStream = new MemoryStream( imageBytes, false ) )
{
Bitmap image = new Bitmap( imageStream );
}

A Generic error occurred in GDI+ in Bitmap.Save method

I am working on to upload and save a thumbnail copy of that image in a thumbnail folder.
I am using following link:
http://weblogs.asp.net/markmcdonnell/archive/2008/03/09/resize-image-before-uploading-to-server.aspx
but
newBMP.Save(directory + "tn_" + filename);
is causing exception "A generic error occurred in GDI+."
I have tried to give permission on folder, also tried to use a new separate bmp object when saving.
Edit:
protected void ResizeAndSave(PropBannerImage objPropBannerImage)
{
// Create a bitmap of the content of the fileUpload control in memory
Bitmap originalBMP = new Bitmap(fuImage.FileContent);
// Calculate the new image dimensions
int origWidth = originalBMP.Width;
int origHeight = originalBMP.Height;
int sngRatio = origWidth / origHeight;
int thumbWidth = 100;
int thumbHeight = thumbWidth / sngRatio;
int bannerWidth = 100;
int bannerHeight = bannerWidth / sngRatio;
// Create a new bitmap which will hold the previous resized bitmap
Bitmap thumbBMP = new Bitmap(originalBMP, thumbWidth, thumbHeight);
Bitmap bannerBMP = new Bitmap(originalBMP, bannerWidth, bannerHeight);
// Create a graphic based on the new bitmap
Graphics oGraphics = Graphics.FromImage(thumbBMP);
// Set the properties for the new graphic file
oGraphics.SmoothingMode = SmoothingMode.AntiAlias; oGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
// Draw the new graphic based on the resized bitmap
oGraphics.DrawImage(originalBMP, 0, 0, thumbWidth, thumbHeight);
Bitmap newBitmap = new Bitmap(thumbBMP);
thumbBMP.Dispose();
thumbBMP = null;
// Save the new graphic file to the server
newBitmap.Save("~/image/thumbs/" + "t" + objPropBannerImage.ImageId, ImageFormat.Jpeg);
oGraphics = Graphics.FromImage(bannerBMP);
// Set the properties for the new graphic file
oGraphics.SmoothingMode = SmoothingMode.AntiAlias; oGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
// Draw the new graphic based on the resized bitmap
oGraphics.DrawImage(originalBMP, 0, 0, bannerWidth, bannerHeight);
// Save the new graphic file to the server
bannerBMP.Save("~/image/" + objPropBannerImage.ImageId + ".jpg");
// Once finished with the bitmap objects, we deallocate them.
originalBMP.Dispose();
bannerBMP.Dispose();
oGraphics.Dispose();
}
When either a Bitmap object or an Image object is constructed from a
file, the file remains locked for the lifetime of the object. As a
result, you cannot change an image and save it back to the same file
where it originated.
http://support.microsoft.com/?id=814675
A generic error occurred in GDI+, JPEG Image to MemoryStream
Image.Save(..) throws a GDI+ exception because the memory stream is closed
http://alperguc.blogspot.in/2008/11/c-generic-error-occurred-in-gdi.html
EDIT:
just writing from memory...
save to an 'intermediary' memory stream, that should work
e.g. try this one - replace
Bitmap newBitmap = new Bitmap(thumbBMP);
thumbBMP.Dispose();
thumbBMP = null;
newBitmap.Save("~/image/thumbs/" + "t" + objPropBannerImage.ImageId, ImageFormat.Jpeg);
with something like:
string outputFileName = "...";
using (MemoryStream memory = new MemoryStream())
{
using (FileStream fs = new FileStream(outputFileName, FileMode.Create, FileAccess.ReadWrite))
{
thumbBMP.Save(memory, ImageFormat.Jpeg);
byte[] bytes = memory.ToArray();
fs.Write(bytes, 0, bytes.Length);
}
}
This error message is displayed if the path you pass to Bitmap.Save() is invalid (folder doesn't exist etc).
// Once finished with the bitmap objects, we deallocate them.
originalBMP.Dispose();
bannerBMP.Dispose();
oGraphics.Dispose();
This is a programming style that you'll regret sooner or later. Sooner is knocking on the door, you forgot one. You are not disposing newBitmap. Which keeps a lock on the file until the garbage collector runs. If it doesn't run then the second time you try to save to the same file you'll get the klaboom. GDI+ exceptions are too miserable to give a good diagnostic so serious head-scratching ensues. Beyond the thousands of googlable posts that mention this mistake.
Always favor using the using statement. Which never forgets to dispose an object, even if the code throws an exception.
using (var newBitmap = new Bitmap(thumbBMP)) {
newBitmap.Save("~/image/thumbs/" + "t" + objPropBannerImage.ImageId, ImageFormat.Jpeg);
}
Albeit that it is very unclear why you even create a new bitmap, saving thumbBMP should already be good enough. Anyhoo, give the rest of your disposable objects the same using love.
In my case the bitmap image file already existed in the system drive, so my app threw the error "A Generic error occured in GDI+".
Verify that the destination folder exists
Verify that there isn't already a file with the same name in the destination folder
Check your folder's permission where the image is saved
Right cLick on folder then go :
Properties > Security > Edit > Add-- select "everyone" and check Allow "Full Control"
I was facing the same issue A generic error occurred in GDI+ on saving while working on MVC app, I was getting this error because I was writing wrong path to save image, I corrected saving path and it worked fine for me.
img1.Save(Server.MapPath("/Upload/test.png", System.Drawing.Imaging.ImageFormat.Png);
--Above code need one change, as you need to put close brackets on Server.MapPath() method after writing its param.
Like this-
img1.Save(Server.MapPath("/Upload/test.png"), System.Drawing.Imaging.ImageFormat.Png);
GDI+ exceptions occured due to below points
Folder access issue
Missing properties of images
If folder issue - please provide access to application
If Missing properties then use below code
Code 1
using (Bitmap bmp = new Bitmap(webStream))
{
using (Bitmap newImage = new Bitmap(bmp))
{
newImage.Save("c:\temp\test.jpg", ImageFormat.Jpeg);
}
}
Code 2
using (Bitmap bmp = new Bitmap(webStream))
{
using (Bitmap newImage = new Bitmap(bmp))
{
newImage.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
Rectangle lockedRect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = newImage.LockBits(lockedRect, ImageLockMode.ReadWrite, bmp.PixelFormat);
bmpData.PixelFormat = bmp.PixelFormat;
newImage.UnlockBits(bmpData);
using (Graphics gr = Graphics.FromImage(newImage))
{
gr.SmoothingMode = SmoothingMode.HighQuality;
gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
}
foreach (var item in bmp.PropertyItems)
{
newImage.SetPropertyItem(item);
}
newImage.Save("c:\temp\test.jpg", ImageFormat.Jpeg);
}
}
Different between code 1 and code 2
Code - 1 : it will just create image and can open it on normal image viewer
the image can't open in Photoshop
Image size will be double
Code - 2 : to open image in image edition tools use code
by using code 1 it just create images but it not assign image marks.
I always check/test these:
Does the path + filename contain illegal characters for the given filesystem?
Does the file already exist? (Bad)
Does the path already exist? (Good)
If the path is relative: am I expecting it in the right parent directory (mostly bin/Debug ;-) )?
Is the path writable for the program and as which user does it run? (Services can be tricky here!)
Does the full path really, really not contain illegal chars? (some unicode chars are close to invisible)
I never had any problems with Bitmap.Save() apart from this list.
I had a different issue with the same exception.
In short:
Make sure that the Bitmap's object Stream is not being disposed before calling .Save .
Full story:
There was a method that returned a Bitmap object, built from a MemoryStream in the following way:
private Bitmap getImage(byte[] imageBinaryData){
.
.
.
Bitmap image;
using (var stream = new MemoryStream(imageBinaryData))
{
image = new Bitmap(stream);
}
return image;
}
then someone used the returned image to save it as a file
image.Save(path);
The problem was that the original stream was already disposed when trying to save the image, throwing the GDI+ exeption.
A fix to this problem was to return the Bitmap without disposing the stream itself but the returned Bitmap object.
private Bitmap getImage(byte[] imageBinaryData){
.
.
.
Bitmap image;
var stream = new MemoryStream(imageBinaryData))
image = new Bitmap(stream);
return image;
}
then:
using (var image = getImage(binData))
{
image.Save(path);
}
I got it working using FileStream, get help from these
http://alperguc.blogspot.in/2008/11/c-generic-error-occurred-in-gdi.html
http://csharpdotnetfreak.blogspot.com/2010/02/resize-image-upload-ms-sql-database.html
System.Drawing.Image imageToBeResized = System.Drawing.Image.FromStream(fuImage.PostedFile.InputStream);
int imageHeight = imageToBeResized.Height;
int imageWidth = imageToBeResized.Width;
int maxHeight = 240;
int maxWidth = 320;
imageHeight = (imageHeight * maxWidth) / imageWidth;
imageWidth = maxWidth;
if (imageHeight > maxHeight)
{
imageWidth = (imageWidth * maxHeight) / imageHeight;
imageHeight = maxHeight;
}
Bitmap bitmap = new Bitmap(imageToBeResized, imageWidth, imageHeight);
System.IO.MemoryStream stream = new MemoryStream();
bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
stream.Position = 0;
byte[] image = new byte[stream.Length + 1];
stream.Read(image, 0, image.Length);
System.IO.FileStream fs
= new System.IO.FileStream(Server.MapPath("~/image/a.jpg"), System.IO.FileMode.Create
, System.IO.FileAccess.ReadWrite);
fs.Write(image, 0, image.Length);
For me it was a permission problem. Somebody removed write permissions on the folder for the user account under which the application was running.
Create folder path image/thumbs on your hard disk => Problem solved!
I used below logic while saving a .png format. This is to ensure the file is already existing or not.. if exist then saving it by adding 1 in the filename
Bitmap btImage = new Bitmap("D:\\Oldfoldername\\filename.png");
string path="D:\\Newfoldername\\filename.png";
int Count=0;
if (System.IO.File.Exists(path))
{
do
{
path = "D:\\Newfoldername\\filename"+"_"+ ++Count + ".png";
} while (System.IO.File.Exists(path));
}
btImage.Save(path, System.Drawing.Imaging.ImageFormat.Png);
I encountered this error while trying to convert Tiff images to Jpeg. For me the issue stemmed from the tiff dimensions being too large. Anything up to around 62000 pixels was fine, anything above this size produced the error.
for me it was a path issue when saving the image.
int count = Directory.EnumerateFiles(System.Web.HttpContext.Current.Server.MapPath("~/images/savedimages"), "*").Count();
var img = Base64ToImage(imgRaw);
string path = "images/savedimages/upImages" + (count + 1) + ".png";
img.Save(Path.Combine(System.Web.HttpContext.Current.Server.MapPath(path)));
return path;
So I fixed it by adding the following forward slash
String path = "images/savedimages....
should be
String path = "/images/savedimages....
Hope that helps anyone stuck!
from msdn: public void Save (string filename); which is quite surprising to me because we dont just have to pass in the filename, we have to pass the filename along with the path for example: MyDirectory/MyImage.jpeg, here MyImage.jpeg does not actually exist yet, but our file will be saved with this name.
Another important point here is that if you are using Save() in a web application then use Server.MapPath() along with it which basically just returns the physical path for the virtual path which is passed in. Something like: image.Save(Server.MapPath("~/images/im111.jpeg"));
I use this solution
int G = 0;
private void toolStripMenuItem17_Click(object sender, EventArgs e)
{
Directory.CreateDirectory("picture");// هذه العملية للرسم بدون ان يحذف بقية الرسومات
G = G + 1;
FormScreen();
memoryImage1.Save("picture\\picture" + G.ToString() + ".jpg");
pictureBox1.Image = Image.FromFile("picture\\picture" + G.ToString() + ".jpg");
}
The code below solved my problem
pictureBox1.Image=myImage;
Bitmap bmp = new Bitmap(pictureBox1.Image);
bmp.Save("C:\\Users/super/Desktop/robin.jpg");

Load large image with memory efficiency

I am using .NET4.5, Windows Forms and C#.
I am loading an image onto a button using:
theButton.BackgroundImage = Image.FromFile("file.png");
The issue is that my button is 128x128 and the image is 4000x8000. The line above consumes very large amounts of memory because file.png is so large.
Does anyone know of a technique I can use to reduce this memory footprint? I am thinking of some function like this:
Image.FromFile(file,width,height);
Any pointers? Thanks.
Yes it works. It's quite simple to resize the image and then display it on button.
But, I don't think that the above code maintains the aspect ratio of the image.
It's quite simple to resize the image with aspect ratio; and then display it on button.
Below is the sample code helps you to resize the image by maintaining the aspect ratio.
You can define a new class or implement the "ResizeImage" method in an existing class. Whichever is comfortable to you.
public class ImageManipulation
{
public static Bitmap ResizeImage(Bitmap originalBitmap, int newWidth, int maxHeight, bool onlyResizeIfWider)
{
if (onlyResizeIfWider)
{
if (originalBitmap.Width <= newWidth)
{
newWidth = originalBitmap.Width;
}
}
int newHeight = originalBitmap.Height * newWidth / originalBitmap.Width;
if (newHeight > maxHeight)
{
// Resize with height instead
newWidth = originalBitmap.Width * maxHeight / originalBitmap.Height;
newHeight = maxHeight;
}
var alteredImage = new Bitmap(originalBitmap, new Size(newWidth, newHeight));
alteredImage.SetResolution(72, 72);
return alteredImage;
}
}
USAGE:
private void DisplayPhoto()
{
// make sure the file is JPEG or GIF
System.IO.FileInfo testFile = new System.IO.FileInfo(myFile);
// Create a new stream to load this photo into
FileStream myFileStream = new FileStream(myFile, FileMode.Open, FileAccess.Read);
// Create a buffer to hold the stream of bytes
photo = new byte[myFileStream.Length];
// Read the bytes from this stream and put it into the image buffer
myStream.Read(photo, 0, (int)myFileStream.Length);
// Close the stream
myFileStream.Close();
// Create a new MemoryStream and write all the information from
// the byte array into the stream
MemoryStream myStream = new MemoryStream(photo, true);
myStream.Write(photo, 0, photo.Length);
// Use the MemoryStream to create the new BitMap object
Bitmap FinalImage = new Bitmap(myStream);
upicPhoto.Image = ImageManipulation.ResizeImage(
FinalImage,
upicPhoto.Width,
upicPhoto.Height,
true);
// Close the stream
myStream.Close();
}
I think your best path here is to just resize the image, to 128x128.
An image that large is always going to take up a lot of memory, no matter what you do with it.
This will also allow you to make the image something that will look good at that size.
This is quite a general problem, AFAIK you have few possibilities
Compress image before uploading , in real world this will not work.
Put a check on size and dimensions of image, in real world it works, even linkedin, facebook they won't allow us to upload images above there specified dimensions.
Use buffering, this is cleanest way you can do in .net
Use some third party plugins or development enviornment, I have done it in Silverlight

Image resize with GDI in .NET gives low saturation

I'm fighting an issue where my resized images looses color saturation when I manipulate them using GDI.
I'm loading an JPG as original, resize it and the resulting image has a lot less saturation (color intensity) than the original picture. What can I do to improve that? this is my code:
using ( var original = System.Drawing.Image.FromStream( new MemoryStream( image.RawData ) ) )
{
using ( var dst = new Bitmap( width, height, PixelFormat.Format32bppArgb ) )
{
using ( var g = Graphics.FromImage( dst ) )
{
g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
g.DrawImage( original, 0, 0, dst.Width, dst.Height );
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage( original, 0, 0, dst.Width, dst.Height );
var jpgEncoder = GetEncoder( ImageFormat.Jpeg );
var myEncoderParameters = new EncoderParameters( 1 );
var quality = 95;
var myEncoderParameter = new EncoderParameter( Encoder.Quality, quality );
myEncoderParameters.Param[0] = myEncoderParameter;
dst.Save( buffer, jpgEncoder, myEncoderParameters );
}
}
}
I've tried with different pixelformats, removing all filters etc but I always get the same result. Is this some known issue with GDI, or have I missed something?
Addon:
When opening the image in Paint.NET, I get the same issue with low saturation, even without rescaling, so I guess it's the way GDI+ loads images (jpgs)?
This image was saved from photoshop with color profile sRGB, but afaik JPG doesn't have info about colorprofiles embedded. And even if it did, I believe that firefox doesn't obey them (which is what I've tested with)
More testing shows that it looks different in IE8 in contrast to firefox. JPGs seems to support color profiles, but most applications doesn't obey them. FF3.5 however, seems to do it. And it was Adobe RGB, not sRGB on the image.
I found the answer myself. It has to do with color-profiles not beeing applied by default in GDI+. Many people claims that you cannot automatically apply color-profiles using GDI, but apparently, the only change I needed to do was this:
using ( var original = System.Drawing.Image.FromStream( new MemoryStream( image.RawData ) ) )
to
using ( var original = new Bitmap( new MemoryStream( image.RawData ), true ) )
Apparently, Bitmap was a derived class of Image, and the constructor for Bitmap can take both a stream aswell as a boolean for "useIcm". That did the trick for me.
I have an image-rescaling code I use, and I don't see the effect you mention.
the main difference I see is that I use Format24bppRgb and not Format24bppARgb
Keep in mind that JPG has no Alpha channel anyway (afaik)
Coming at this from another angle, I would highly recommend ImageResizer if you want to take the pain out of doing this yourself. It even takes care of disk caching.
Been there, done that. Silly as it may sound, this makes difference:
var quality = 95;
var myEncoderParameter = new EncoderParameter( Encoder.Quality, (long)quality );
or
long quality = 95;
var myEncoderParameter = new EncoderParameter( Encoder.Quality, quality );
or
var quality = 95L;
var myEncoderParameter = new EncoderParameter( Encoder.Quality, quality );
Just pick one.

Categories

Resources