How can I insert Image object into FileInfo list?
I have a list with images of this type
public List<FileInfo> _imagesList = new List<FileInfo>();
But I need to add image to this list using a method like this one:
public void AddImage(Image img)
{
}
I tried using this method
public void AddImage(string pathToImage)
{
try
{
FileInfo file = new FileInfo(pathToImage);
_imagesList.Add(file);
}
catch (Exception e)
{
MessageBox.Show("Не удалось загрузить изображение. Ошибка : " + e);
}
}
Let me explain why you can't do that. Image class does not have any references to file which was used to create that image. When you create Image instance with
Image.FromFile(filename)
File name is not stored anywhere. All you will have inside Image instance is an array of bytes which is initialized this way:
Stream dataStream = File.OpenRead(filename);
image.rawData = new byte[(int) dataStream.Length];
dataStream.Read(image.rawData, 0, (int) dataStream.Length);
So, the point is - you can't get file name from which Image was created. Actually image can be create not from file - from any stream (network stream, memory stream). Image is not related to any file, thus you can't insert it to list of FileInfo objects.
Well, as a workaround, you can save Image to file. And then insert that file to list.
you can either use the method you gave, adding new FileInfo(pathToImage) or change your list so it'll contain Image items
you can use
public List<Image> _imagesList = new List<Image>();
for example. if you share more information i could help you more. this looks like an XY problem so maybe you should tell us what's the real problem
You cannot do like this, FileInfo accept one arguments in its constructor: fileName.
You should have something like this:
public List<FileInfo> _imagesList = new List<FileInfo>();
public void Main()
{
AddImage("YourImagePath1");
AddImage("YourImagePath2");
// ...
}
public void AddImage(string path)
{
_imagesList.Add(new FileInfo(path));
//var img = Image.FromFile(path); // do this if you need to load your image
}
UPDATE:
You changed your question and your code now looks like mine, what was the issue in the code you used to have?
Related
I serialize images using the following code:
public static string SerializeImage(Image image)
{
using (MemoryStream memoryStream = new MemoryStream())
{
image.Save(memoryStream, image.RawFormat);
return Convert.ToBase64String(memoryStream.ToArray());
}
}
and deserialize the images by doing the following
public static Image DeserializeImage(string serializedImage)
{
byte[] imageAsBytes = Convert.FromBase64String(serializedImage);
using (MemoryStream memoryStream = new MemoryStream(imageAsBytes, 0, imageAsBytes.Length))
{
memoryStream.Write(imageAsBytes, 0, imageAsBytes.Length);
return Image.FromStream(memoryStream, true);
}
}
If I have an image and does
string serializedImage1 = SerializeImage(image);
Image deserializedImage = DeserializeImage(serializedImage1);
string serializedImage2 = SerializeImage(deserializedImage );
Then
serializedImage1 == serializedImage2;
as expected. But it is not always the case.
If I serialize an image on Process 1, and then redeserialize and reserialize it on Process 2, then the result of the reserialization on Process 2 is not the same as on the Process 1. Everything works, but a few bytes in the beginning of the serialization are different.
Worst, if I do the same thing on 2 different dll (or thread, I'm not sure), it seems the serialization result is not the same too. Again, the serialization/deserialization works, but a few bytes are different.
The image the first time is loaded with the following function :
public static Image GetImageFromFilePath(string filePath)
{
var uri = new Uri(filePath);
var bitmapImage = new BitmapImage(uri);
bitmapImage.Freeze();
using (var memoryStream = new MemoryStream())
{
var pngBitmapEncoder = new PngBitmapEncoder();
pngBitmapEncoder.Frames.Add(BitmapFrame.Create(bitmapImage));
pngBitmapEncoder.Save(memoryStream);
Image image = Image.FromStream(memoryStream);
return image;
}
}
Note however that it happens even if the image is loaded twice with the DeserializeImage() function.
The tests I have done are with ImageFormat.Jpeg and ImageFormat.Png.
First question, why it does this ? I would have expected the result to be always the same, but I suppose some salt is used when doing the Image.Save().
Second question : I want to have a deterministic way to serialize an image, keeping the image format intact. The goal is to save the image in a DB and also to compare serialized images to know if it already exists in the system where this function is used.
Well, I discovered while trying to solve this that a png or jpeg Image object inside C# has some metadata associated to it and doing what I was doing is just not a reliable way to compare images.
The solution I used was derived from this link
https://insertcode.wordpress.com/2014/05/13/compare-content-of-two-files-images-in-c/
So what I do finally is save the images inside the system with the SerializeImage(Image image) function previously described, and when I want to consume it I deserialize it with the DeserializeImage(string serializedImage) function previously described. But when I want to compare images I use the following functions
public static bool ImagesAreEqual(Image image1, Image image2)
{
string image1Base64Bitmap = GetImageAsBase64Bitmap(image1);
string image2Base64Bitmap = GetImageAsBase64Bitmap(image2);
return image1Base64Bitmap.Equals(image2Base64Bitmap);
}
public static string GetImageAsBase64Bitmap(Image image)
{
using (var memoryStream = new MemoryStream())
{
using (var bitmap = new Bitmap(image))
{
bitmap.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Bmp);
}
return Convert.ToBase64String(memoryStream.ToArray());
}
}
That convert the image to raw bitmap before comparing them.
This does a perfect job for me in all my needed cases : the formats of the images are saved/restored correctly, and I can compare them between them to check if they are the same without having to bother with the possibly different serialization.
I am using WPF/C# in VS2017.
My code takes a path to an image file as a string and tries to load it into a BitmapFrame, which has worked fine mostly, except someone tested it with some image files from a Mac and it threw an exception. It turns out that the filename contains the character #F025, which in Windows is just displayed as a thick dot.
The files are photos, named from the photograph title. This one had a question mark in it, but ended up with #F025.
Anyway, the code converts the string to a Uri and then uses BitmapFrame as follows:
public new void Init()
{
Source = new Uri(Path);
fi = new FileInfo(Path);
BitmapImage bs;
try
{
Image = BitmapFrame.Create(Source);
Metadata = new ExifMetadata(Image);
}
catch (Exception e)
{
Error = e.Message;
IsBroken = true;
}
When I look at the Uri "Source" in the debugger, then #F025 character has been replaced with "%25EF%2580%25A5". The Create function throws a "File Not Found" exception.
If there any way of converting this to a Uri that the Create function will accept, or perhaps just using the string Path to load it instead? I did try using a stream with the filename but that didn't work either.
TIA.
You may try to create the BitmapFrame from a FileStream. Note that BitmapCacheOption.OnLoad must be set in order to be able to close the stream immediately after the Create call.
using (var stream = File.OpenRead(Path))
{
Image = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
I'm reading in a .docx file using the Novacode API, and am unable to create or display any images within the file to a WinForm app due to not being able to convert from a Novacode Picture (pic) or Image to a system image. I've noticed that there's very little info inside the pic itself, with no way to get any pixel data that I can see. So I have been unable to utilize any of the usual conversion ideas.
I've also looked up how Word saves images inside the files as well as Novacode source for any hints and I've come up with nothing.
My question then is is there a way to convert a Novacode Picture to a system one, or should I use something different to gather the image data like OpenXML? If so, would Novacode and OpenXML conflict in any way?
There's also this answer that might be another place to start.
Any help is much appreciated.
Okay. This is what I ended up doing. Thanks to gattsbr for the advice. This only works if you can grab all the images in order, and have descending names for all the images.
using System.IO.Compression; // Had to add an assembly for this
using Novacode;
// Have to specify to remove ambiguous error from Novacode
Dictionary<string, System.Drawing.Image> images = new Dictionary<string, System.Drawing.Image>();
void LoadTree()
{
// In case of previous exception
if(File.Exists("Images.zip")) { File.Delete("Images.zip"); }
// Allow the file to be open while parsing
using(FileStream stream = File.Open("Images.docx", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using(DocX doc = DocX.Load(stream))
{
// Work rest of document
// Still parse here to get the names of the images
// Might have to drag and drop images into the file, rather than insert through Word
foreach(Picture pic in doc.Pictures)
{
string name = pic.Description;
if(null == name) { continue; }
name = name.Substring(name.LastIndexOf("\\") + 1);
name = name.Substring(0, name.Length - 4);
images[name] = null;
}
// Save while still open
doc.SaveAs("Images.zip");
}
}
// Use temp zip directory to extract images
using(ZipArchive zip = ZipFile.OpenRead("Images.zip"))
{
// Gather all image names, in order
// They're retrieved from the bottom up, so reverse
string[] keys = images.Keys.OrderByDescending(o => o).Reverse().ToArray();
for(int i = 1; ; i++)
{
// Also had to add an assembly for ZipArchiveEntry
ZipArchiveEntry entry = zip.GetEntry(String.Format("word/media/image{0}.png", i));
if(null == entry) { break; }
Stream stream = entry.Open();
images[keys[i - 1]] = new Bitmap(stream);
}
}
// Remove temp directory
File.Delete("Images.zip");
}
I am currently working on a 2D Game Engine, so far I have added a feature to load and remove images, but now I want to save them, this is the dictionary:
public Dictionary<string, Image> images = new Dictionary<string, Image>();
The string is the name, for example when the person wants to add an image they click a button that says choose image then a pitcurebox will be set as the image that was opened, then there is a textbox, when they click a button that says Add Image, it will do this images.Add(textBox1.Text, pictureBox1.Image) Then I want to save all the images added to a folder
I have looked all over the internet for this, but no one has an answer for me, and I'm really stuck, thanks in advance.
The Image class has the Save method. So you could do something like this:
foreach (var imgX in images.Select(kvp => kvp.Value))
{
imgX.Save("figure_a_file_path_and_name", ImageFormat.Jpeg);
}
UPDATE
If you would like to use the string in the dictionary as file name change the above code a little bit:
var folder = "figure_the_folder_path\\";
foreach (var entry in images)
{
var destinationFile = string.Concat(folder, entry.Key);
var img = entry.Value;
img.Save(destinationFile, ImageFormat.Jpeg);
}
Save image to single folder with BinaryFormatter to serialize/deserialize your dictionary to file like this:
public void Save(string filename)
{
var bin_formater = new BinaryFormatter();
using (var stream = File.Create(filename))
{
bin_formater.Serialize(stream, images); //images is your dictionary
}
}
i have written a programm that uses some picture ressources and creates a file from them.
As a Customer preview the images are shown before they get worked with. Therefore i need to dispose them before i can work with them. But i want to load the preview again afterwards and since it might be a huge number of pictures I want to do it dynamically, there i fail.
I use a Dictionary with the filename as String:
//Creating a Bitmap so we can preview the Picture.
try
{
mainDrawingBackgroundBitmap = new Bitmap(filePathBackgroundBackgroundImage);
if(mainAllBitmapsDictionary.ContainsKey(mainDrawingBackgroundBitmap))
{
mainAllBitmapsDictionary.Remove(mainDrawingBackgroundBitmap);
}
mainAllBitmapsDictionary.Add(mainDrawingBackgroundBitmap,filePathBackgroundBackgroundImage);
}
Now my Bitmap and path are in the Dictionary, later i dispose them this way:
private void DisposeAllFiles()
{
foreach (var tempBitmap in mainAllBitmapsDictionary.Keys)
{
tempBitmap.Dispose();
}
}
Which works just fine.
Now when i try to recreate the Bitmaps:
private void RessurrectAllFiles()
{
foreach (var tempAllBitmap in mainAllBitmapsDictionary)
{
try
{
var tempBitmap = tempAllBitmap.Key;
tempBitmap = new Bitmap(tempAllBitmap.Value);
}
catch (ArgumentException ae)
{
}
}
}
He doesn't fail or throw an error, the Dictionary even gets filled with Correct Bitmaps and strings but, those don't seem to affect the original objects anymore, so the Dictionary is as it was but when i inspect a Bitmap like the: mainDrawingBackgroundBitmap, i only see ArgumentExceptions.
To put it bluntly, where do i fail?
Keep path to images as keys, and bitmap data as values, so then it will be easy to you to manipulate data and searching dictionary will perform fast.
Dictionary<string, Bitmap>