Handling digital camera images with System.Drawing
-
I have a web application that allows users to upload images. On upload, the image is saved to disk, then passed to a routine that crops and resizes it to a specified size, using the System.Drawing.Graphics/Imaging classes - all pretty standard stuff, and it works. Usually. Every now and then it fails, and the only error message I can catch is "A generic error occurred in GDI+." Looking at the files uploaded, my deduction (call me Sherlock) is that they are images from digital cameras, and that it may be the embedded photo information within such files that is causing the problem. I note that if I download them (via FTP), then open them in Paint.NET (say) then save them (without making any (apparent) changes), I can then load them via my application without this error occuring. I would be grateful if anyone has any ideas about what I need to do to enable my application to handle these files without error. Thanks...
-
I have a web application that allows users to upload images. On upload, the image is saved to disk, then passed to a routine that crops and resizes it to a specified size, using the System.Drawing.Graphics/Imaging classes - all pretty standard stuff, and it works. Usually. Every now and then it fails, and the only error message I can catch is "A generic error occurred in GDI+." Looking at the files uploaded, my deduction (call me Sherlock) is that they are images from digital cameras, and that it may be the embedded photo information within such files that is causing the problem. I note that if I download them (via FTP), then open them in Paint.NET (say) then save them (without making any (apparent) changes), I can then load them via my application without this error occuring. I would be grateful if anyone has any ideas about what I need to do to enable my application to handle these files without error. Thanks...
is it specific files that fail consistently? how about you try it, and if it fails, you try again say 5 seconds later. I have a theory... :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.
-
is it specific files that fail consistently? how about you try it, and if it fails, you try again say 5 seconds later. I have a theory... :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.
Well, always JPG files - tried your suggestion, but it didn't work. But it got me thinking, and I did track down the error to the actual line, which was where the modified file was being saved. I had written the code to always save the modified file in the same image format as the original, but decided to try always saving it as a PNG file instead - and this seems to have solved the problem! So, thanks anyway - not 100% sure what for exactly :-) but I've been stuck on this for ages, and now at least I have a working solution!
-
Well, always JPG files - tried your suggestion, but it didn't work. But it got me thinking, and I did track down the error to the actual line, which was where the modified file was being saved. I had written the code to always save the modified file in the same image format as the original, but decided to try always saving it as a PNG file instead - and this seems to have solved the problem! So, thanks anyway - not 100% sure what for exactly :-) but I've been stuck on this for ages, and now at least I have a working solution!
Almost everything that goes wrong in Image.Save (and a few more methods) will result in a "General GDI+ error". Since you now decided to provide some of the essential information (but no code) to figure out which of many possible causes is most likely, my best guess is you did not close the input file. Some ways of doing that is by loading an image in a PictureBox (PB.ImageLocation=...), or by loading an image from file (Image.FromFile or Image.FromStream). Using a different output format and file extension is a sure way to avoid those problems, but it also tells me the core problem is still present, resulting in less than optimal behavior of your app as it will keep files open and images in memory much longer than required. You should fix that. You're welcome anyway. :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.
-
Almost everything that goes wrong in Image.Save (and a few more methods) will result in a "General GDI+ error". Since you now decided to provide some of the essential information (but no code) to figure out which of many possible causes is most likely, my best guess is you did not close the input file. Some ways of doing that is by loading an image in a PictureBox (PB.ImageLocation=...), or by loading an image from file (Image.FromFile or Image.FromStream). Using a different output format and file extension is a sure way to avoid those problems, but it also tells me the core problem is still present, resulting in less than optimal behavior of your app as it will keep files open and images in memory much longer than required. You should fix that. You're welcome anyway. :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.
OK, thanks... I didn't post code because I hoped a pointer might be enough for me, but... and then I found a working slution. But still, it would be good to know the "proper" way to solve it! If you can stand looking at some VB code, and don't mind giving it the once over, that'd be great - but as I say, I at least have a working solution, so... also, just to clarify: my issue only occurs with jpg files (AFAIK), but I didn't mean to imply that it always happens with these - just that when it does, it's ben with jpg's.
Public Function ResizeCropImage(ByVal sFilepath As String, ByVal ext As String) As Integer
' sFilepath = relative path to file, including it's name; ext = file extension
' this file has already been saved from File Upload control via it's usual SaveAs method
Try
Dim FullSizeImage As Image
FullSizeImage = Image.FromFile(HttpContext.Current.Server.MapPath(sFilepath))If (FullSizeImage.Height < 200) Or (FullSizeImage.Width < 200) Then FullSizeImage.Dispose() ' image is too small System.IO.File.Delete(HttpContext.Current.Server.MapPath(sFilepath)) Return 0 End If Dim imf As System.Drawing.Imaging.ImageFormat Select Case ext Case ".gif" imf = System.Drawing.Imaging.ImageFormat.Gif Case ".png" imf = System.Drawing.Imaging.ImageFormat.Png Case Else '".jpg", ".jpeg" imf = System.Drawing.Imaging.ImageFormat.Jpeg End Select Dim tmp As Bitmap Dim gX As System.Drawing.Graphics Dim bTidy As Boolean = True If (FullSizeImage.Height = 200) And (FullSizeImage.Width = 200) Then 'nothing to do bTidy = False Else ' resize/crop to 200 x 200 Select Case FullSizeImage.Height / FullSizeImage.Width Case Is < 1 'landscape image Dim x1 As Integer tmp = New Bitmap(200, 200) tmp.SetResolution(FullSizeImage.HorizontalResolution, FullSizeImage.VerticalResolution) x1 = CInt((FullSizeImage.Width / 2) - (FullSizeImage.Height / 2)) gX = System.Drawing.Graphics.FromImage(tmp) gX.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic gX.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality
-
OK, thanks... I didn't post code because I hoped a pointer might be enough for me, but... and then I found a working slution. But still, it would be good to know the "proper" way to solve it! If you can stand looking at some VB code, and don't mind giving it the once over, that'd be great - but as I say, I at least have a working solution, so... also, just to clarify: my issue only occurs with jpg files (AFAIK), but I didn't mean to imply that it always happens with these - just that when it does, it's ben with jpg's.
Public Function ResizeCropImage(ByVal sFilepath As String, ByVal ext As String) As Integer
' sFilepath = relative path to file, including it's name; ext = file extension
' this file has already been saved from File Upload control via it's usual SaveAs method
Try
Dim FullSizeImage As Image
FullSizeImage = Image.FromFile(HttpContext.Current.Server.MapPath(sFilepath))If (FullSizeImage.Height < 200) Or (FullSizeImage.Width < 200) Then FullSizeImage.Dispose() ' image is too small System.IO.File.Delete(HttpContext.Current.Server.MapPath(sFilepath)) Return 0 End If Dim imf As System.Drawing.Imaging.ImageFormat Select Case ext Case ".gif" imf = System.Drawing.Imaging.ImageFormat.Gif Case ".png" imf = System.Drawing.Imaging.ImageFormat.Png Case Else '".jpg", ".jpeg" imf = System.Drawing.Imaging.ImageFormat.Jpeg End Select Dim tmp As Bitmap Dim gX As System.Drawing.Graphics Dim bTidy As Boolean = True If (FullSizeImage.Height = 200) And (FullSizeImage.Width = 200) Then 'nothing to do bTidy = False Else ' resize/crop to 200 x 200 Select Case FullSizeImage.Height / FullSizeImage.Width Case Is < 1 'landscape image Dim x1 As Integer tmp = New Bitmap(200, 200) tmp.SetResolution(FullSizeImage.HorizontalResolution, FullSizeImage.VerticalResolution) x1 = CInt((FullSizeImage.Width / 2) - (FullSizeImage.Height / 2)) gX = System.Drawing.Graphics.FromImage(tmp) gX.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic gX.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality
Hi, I assume this is the code that includes your work-around, not the one that fails occasionally. If you remove the extension modifications, it may fail because, under some conditions, you attempt an Image.Save whereas your original image still is alive since FullSizeImage.Dispose() only occurs later. The fix is to move FullSizeImage.Dispose() up and put it in between gX.DrawImage() and tmp.Save() I have some more comments: 1. your big three-way switch is unnecessary, you could handle portrait/landscape/square images all the same by using something like:
x1 = CInt((FullSizeImage.Width / 2) - (FullSizeImage.Height / 2)) y1=-x1 if x1<0 then x1=0 if y1<0 then y1=0
2. I don't think you need bTidy at all. 3. I'm puzzled by your call to SetAttributes; it seems very likely you either don't need it at all, or you also need it when the input image is "too small". 4. I would suggest you replace all those magic "200" constants by a single variable, and probably make it a function parameter. Overall I would suggest you look for simpler ways to organize your code; anytime you duplicate statements (or constants) you should try and find a better way, the DRY principle (Don't Repeat Yourself) is pretty universal. :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.
-
Hi, I assume this is the code that includes your work-around, not the one that fails occasionally. If you remove the extension modifications, it may fail because, under some conditions, you attempt an Image.Save whereas your original image still is alive since FullSizeImage.Dispose() only occurs later. The fix is to move FullSizeImage.Dispose() up and put it in between gX.DrawImage() and tmp.Save() I have some more comments: 1. your big three-way switch is unnecessary, you could handle portrait/landscape/square images all the same by using something like:
x1 = CInt((FullSizeImage.Width / 2) - (FullSizeImage.Height / 2)) y1=-x1 if x1<0 then x1=0 if y1<0 then y1=0
2. I don't think you need bTidy at all. 3. I'm puzzled by your call to SetAttributes; it seems very likely you either don't need it at all, or you also need it when the input image is "too small". 4. I would suggest you replace all those magic "200" constants by a single variable, and probably make it a function parameter. Overall I would suggest you look for simpler ways to organize your code; anytime you duplicate statements (or constants) you should try and find a better way, the DRY principle (Don't Repeat Yourself) is pretty universal. :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, and improve readability.
No, this is the "old" code that fails... 1. yes, I know... 2. probably 3. yes - it was jst something I tried to (maybe) help, but you're right 4. well, yes, but for thsi app that isn't necessary as it will remain fixed thanks..