I am creating a photo editing app for my c# project. I need to support different image formats, but to begin with, I chose a very simple format - PNM. At the moment, I can open PNM images and change the color scheme, but strange things happen when saving. When saving the image on macOS, everything works fine, but after trying a similar action on Windows, I got a strange effect, the colors were read with a shift of 1 byte with each new opening after saving.
Here is an example of an image:
example image
That’s what is happening after loading and re-opening it on Windows:
damaged image
It is logical to assume that the problem is in the methods of saving the image. I first write the entire file header to the stream, and then write the color bytes one by one.
I can't figure out what exactly the problem is, because when debugging, the array of bytes being written contained nothing extra. Opening the image in the notepad before and after saving, I did not find any visible changes.
Here is my implementation of saving in a file:
public void SaveTo(Stream stream)
{
_filter.WriteHeader(stream, _image);
foreach (var (x, y) in _enumerationStrategy.Enumerate(Width, Height))
{
var triplet = _image[x, y];
_filter.Write(stream, triplet, _converterProvider.Converter);
}
}
public void WriteHeader(Stream stream, IBitmapImage image)
{
var builder = new StringBuilder();
builder.AppendLine("P6");
builder.AppendLine($"{image.Width} {image.Height}");
builder.AppendLine("255");
var header = builder.ToString();
var headerBytes = Encoding.UTF8.GetBytes(header);
stream.Write(headerBytes, 0, headerBytes.Length);
}
I tried to create a header of image without a string builder, and I tried to write the header to the stream in different ways. Trying different encodings to convert bytes didn't work out too. The effect is the same...
I guess your issue is caused by inconsistent line endings between operating systems. When you use AppendLine it adds a \n character after your string which is translated to binary differently, depending on the OS you running.
I suggest you to write a line ending directly like so:
builder.Append((char)10);
Sorry for my bad English.
I would like to extract text from a image then delete it. Can you show me how to stop Ocr process after i extracted text from file to delete it?
this is my code
Image image = Image.FromFile(#"C:\Users\Admin\Desktop\testtool.png");
var ocr = new AutoOcr();
result_text.Text = ocr.Read(image).ToString();
string textforClipboard = result_text.Text.Replace("\n", Environment.NewLine);
Clipboard.Clear();
Clipboard.SetText(textforClipboard);
//File.Delete(#"C:\Users\Admin\Desktop\testtool.png");
This is the error:
System.IO.IOException: 'The process cannot access the file 'C:\Users\Admin\Desktop\testtool.png' because it is being used by another process.'
An image is Disposable. Before you delete files that contains Images, you should Dispose all Images that are extract from the file.
Make it good practice: always consider using when you are dealing with an IDisposable. This way, you can be certain that whatever happens: the object is disposed when you don't need it anymore, even after exceptions.
using (Image image = Image.FromFile(#"C:\Users\Admin\Desktop\testtool.png"))
{
// do with the image what you need to do
// Is AutoOcr also IDisposable?
using (var autoOcr = new AutoOcr())
{
...
}
// object autoOcr is disposed
}
// object image is disposed. You can delete the file that contains this image
System.IO.File.Delete(...);
Having a code that works for ages when loading and storing images, I discovered that I have one single image that breaks this code:
const string i1Path = #"c:\my\i1.jpg";
const string i2Path = #"c:\my\i2.jpg";
var i = Image.FromFile(i1Path);
i.Save(i2Path, ImageFormat.Jpeg);
The exception is:
System.Runtime.InteropServices.ExternalException occurred
A generic error occurred in GDI+.
at System.Drawing.Image.Save(String filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
at System.Drawing.Image.Save(String filename, ImageFormat format)
at ...
As far as I can see, there is nothing special about the image. It is approx 250 pixels in size and can be opened in e.g. Windows Image Viewer or Paint.NET:
(Since the image above, after being uploaded to Stack Overflow does not produce the error anymore, I've put the original image here)
What I discovered is that upon calling the Save method, the destination image file is being created with zero bytes.
I am really clueless on what causes the error.
My questions:
Can you think of any special thing that would hinder .NET from saving the image?
Is there any way (beside panicing) to narrow down these kind of errors?
While I still did not find out the reason what exactly caused the error when saving the image, I found a workaround to apply:
const string i1Path = #"c:\my\i1.jpg";
const string i2Path = #"c:\my\i2.jpg";
var i = Image.FromFile(i1Path);
var i2 = new Bitmap(i);
i2.Save(i2Path, ImageFormat.Jpeg);
I.e. by copying the image internally into a Bitmap instance and saving this image instead of the original image, the error disappeared.
I'm assuming that by copying it, the erroneous parts the caused the original Save call to fail are being removed an/or normalized, thus enabling the save operation to succeed.
Interestingly, the so stored image has a smaller file on disk (16 kB) than its original source (26 kB).
First of all make sure, that the desired folder has Read/Write permissions. Changing the permissions solved this problem for me.
Solution is here, you must dispose image object to release the memory on the server.
Try use using statement. Make sure destination directory on server exists too.
The reason may be that the image is loaded lazily and the loading process is not yet finished when you try to save it.
Following what's said in this blog post (assuming you're German by the picture you linked in your question) provides a possible solution. Also this SO question's accepted answer indicates this is due to the fact the image file you're trying to save to is locked.
EDIT
For Ulysses Alves, from the linked blog entry: If you load an image using Image.FromFile() it remains locked until it is disposed of. This prevents calls to Save().
pictureBox1.Image = Image.FromFile("C:\\test\\test1.jpg");
pictureBox1.Image.Save("C:\\test\\test2.jpg");
The above code throws an error.
To make it work, you need to copy the image. The following code works:
pictureBox1.Image = Image.FromFile("C:\\test\\test1.jpg");
Image copy = pictureBox1.Image;
copy.Save("C:\\test\\test2.jpg")
I found this question because I also faced the similar error and the file was actually created with zero length (if you don't see any file, first check the permissions to write into folder as other answers suggest). Although my code was slightly different (I use stream to read the image from memory, not from file), I think my answer may be helpful to anyone facing similar problem.
It may looks counter-intuitive, but you can't really dispose memory stream until you finish with image.
NOT WORKING:
Image patternImage;
using (var ms = new MemoryStream(patternBytes)) {
patternImage = new Bitmap(ms);
}
patternImage.Save(patternFile, ImageFormat.Jpeg);
Just don't dispose the stream until you done with image.
WORKS:
using (var ms = new MemoryStream(patternBytes)) {
patternImage = new Bitmap(ms);
patternImage.Save(patternFile, ImageFormat.Jpeg);
}
What is misleading:
Error message doesn't really tell you anything
You can see the image properties, like width and height, but can't
save it
my solution was to make, write temp content (File.WriteAllText) just before saving the file
Here is the code:
var i = Image.FromFile(i1Path);
File.WriteAllText(i2Path, "empty"); // <---- magic goes here
i.Save(i2Path, ImageFormat.Jpeg);
Please try and let me know
In my case I have accidentally deleted the directory where image was getting stored.
Key Information:
// Using System.Drawing.Imaging:
new Bitmap(image).Save(memoryStream, ImageFormat.Jpeg);
You MUST Cast the Image to a Bitmap to Save it.
Using:
// Using System.Drawing.Imaging:
image.Save(memoryStream, ImageFormat.Jpeg);
WILL throw the Error:
Generic GDI+ error when saving an image
Just use the visual studio as administrator or run the application created by the code as administrator it should work smoothly.
It is user access rights issue.
I faced the same and resolved it by running visual studio as administrator.
In my case, I set validateImageData to false:
Image.FromStream(stream, validateImageData: false);
solution:
Image.FromStream(stream, validateImageData: true);
Open in the program
const string i1Path = #"c:\my\i1.jpg";
const string i2Path = #"c:\my\i2.jpg";
var i = Image.FromFile(i1Path);
i.Save(i2Path, ImageFormat.Jpeg);
i.Dispose();
I need to know the destination size of an image before imageresizer actually perform the re-size. And I don't want to open the image file.
I have the original image size and the imageresizer parameters.
I want to write an img tag this way:
<img src="/files/rb45_45667.jpg?height=150&quality=90" width="XXX" height="YYY">
I tried with ImageState
var imagestate = new ImageState(new ResizeSettings("height=150;quality=90"), new Size(1000,500), true);
imagestate.destSize = PolygonMath.RoundPoints(imagestate.layout.GetBoundingBox().Size);
but this wont work because i think I should perform all the ImageBuilder Layout protected methods:
protected override RequestedAction Layout(ImageState s) {
if (base.Layout(s) == RequestedAction.Cancel) return RequestedAction.Cancel;
FlipExistingPoints(s); //Not implemented
LayoutImage(s);
PostLayoutImage(s);
LayoutPadding(s);
PostLayoutPadding(s);
LayoutBorder(s);
PostLayoutBorder(s);
LayoutEffects(s);
PostLayoutEffects(s);
LayoutMargin(s);
PostLayoutMargin(s);
LayoutRotate(s);
PostLayoutRotate(s);
LayoutNormalize(s);
PostLayoutNormalize(s);
LayoutRound(s);
PostLayoutRound(s);
EndLayout(s);
return RequestedAction.None;
}
But this is impossible without opening the image file...
In the ImageState documentation they says:
Encapsulates the state of an image being resized. Can be used to simulate a resize as well as actually perform one.
But i don't find a way how do it.
I find a simple way to do it.
I don't know if it is the right way to do it but it works:
ImageBuilder.Current.GetFinalSize(ImageSize, new ResizeSettings(parameters));
I've been learning quite a bit from everyone here, however I've come across a problem which I'm having trouble finding an answer to. While attempting to save an image which has been uploaded using AsyncFileUpload I'm receiving the following error: A generic error occurred in GDI+.
I've done quite a bit of Googling and it seems that the most likely issue is permissions related. I've tried allowing write/modify for Network and Network_Serice. Later attempted allowing it for the environment user and finally tested it with "Everyone" - unfortunately it was still not working.
One possible issue is the fact that I'm relying on the built-in development server. I'm still fairly new to it all, so not too sure if there are any limitations in this area or if I'm simply overlooking something obvious.
Code:
try
{
//Attempt to convert file to image
uploadedImage = System.Drawing.Image.FromStream(avatarUpload.PostedFile.InputStream);
//Save image
if (uploadedImage != null)
{
//Create vars
String savePath = Server.MapPath("~/Images/Avatars/");
//Save image
uploadedImage.Save(savePath, ImageFormat.Jpeg); !!! Exception raised here
//Dispose of image
uploadedImage.Dispose();
}
}
catch
{
//Create error message
createMessage("Please ensure you select an image i.e. .bmp, .png, .jpg.", "error");
}
If anyone has any ideas or needs any more info please let me know.
Change
String savePath = Server.MapPath("~/Images/Avatars/");
To
String savePath = Server.MapPath("~/Images/Avatars/myImage.jpg");