I'm using an image processing API which resizes and reformats an image from jpg to png. During this process the image loses the metadata.
Is there a way to write the metadata back to file.
I can get the xmp metadata from the original but now I need to write it back to the new file.
This is my code so far using the metadata-extractor framework for .Net
public static void ReadXmpData(string fileLocation)
{
var xmpDirectory = ImageMetadataReader.ReadMetadata(fileLocation).OfType<XmpDirectory>().FirstOrDefault();
foreach (var property in xmpDirectory.XmpMeta.Properties)
{
Console.WriteLine($"Path:\t{(property.Path != null ? property.Path.Trim() : property.Path)}\n" +
$"Namespace:\t{(property.Namespace != null ? property.Namespace.Trim() : property.Namespace)}\n" +
$"Value:\t{(property.Value != null ? property.Value.Trim() : property.Value)} \n\n\n");
}
}
Any help or guidance would be appreciated.
Edit: I am aware that metadata-extractor does not yet support the writing of xmp data
Use ImageMagick.net which will do image transformations without destroying the exif or xmp metadata. I use this for major film/tv studio images and works great.
Related
I try to save my images on my server, but I can't let my server save file and virus because of that I want to get image content as pixels of rgb and after that I create image by myself.
I can't use bitmap (or other type in C# like bitmapImage, ... etc) and I don't know how I can do this with sixlabors.ImageSharp.
I have some code that I tried but I can't implement the exact logic that I want (code shown here):
[HttpPost("[action]")]
public async Task<IActionResult> Get([FromForm] ImageFormat file)
{
await using var memoryStream = new MemoryStream();
await file.File.CopyToAsync(memoryStream);
IImageFormat format;
using (var image = Image.Load(memoryStream.ToArray(), out format))
{
using (var output = new MemoryStream())
{
image.Save(output, format);
var responseType = format.Name.ToLower();
return File(output.ToArray(), "application/octet-stream", file.File.FileName);
}
}
return null;
}
Can anybody help me with this problem?
i don't see a reason to convert image into image: there are several format zip-algorythms etc.wich you have to support in that case. example jpg is not bitmap, there is convertion issue - quality of image becomes less each conversion time. Image itself is not executable - it can be used only as container for virus body, can't harm your OSystem itself, another executable part should works somewhere.
But even if you would like to store images on disk, in other format - you can convert image to base64 text (one line of code, like example) - it less harmful and well known way to work with any file type. you can zip image by cszip, you can change file name and extension to hide file type.
I don't see a reasson to convert one image to another for this scenario/task.
I have a requirement to insert a unique ID into image files without modifying the image content – ie it’s just the metadata that I want to modify. I’m starting with the JPEG files because there is an appropriate EXIF property available: ImageUniqueID.
I’m using C# with .NET Core 3.1 for this exercise with ImageSharp.
I can change the EXIF data with the ImageSharp using the following code (show simplified without existing record checks, etc):
using (var image = Image.Load(filename))
{
var imageUniqueIdentifier = Guid.NewGuid().ToString().ToLower().Replace("-", "");
image.Metadata.ExifProfile.SetValue(ExifTag.ImageUniqueID, imageUniqueIdentifier);
var jpegEncoder = new JpegEncoder() { Quality = 100 };
image.Save(filename, jpegEncoder);
}
I did play with the Quality setting in the JpegEncoder, but was still getting either unacceptable quality degradation or file size increases.
Is there a way of just reading the meta data, altering it and then writing it back without affecting the image at all?
I also looked at MetadataExtractor.NET but this doesn’t have a write facility and would happily look at other .NET Core methods or libraries.
After some research I've found that there is ExifLibrary which allow you to modify only image metadata.
Documentation (examples included)
Example how to add unique image id for jpg file:
var file = ImageFile.FromFile("path_to_jpg_file");
var imageUniqueIdentifier = Guid.NewGuid().ToString().ToLower().Replace("-", "");
file.Properties.Set(ExifLibrary.ExifTag.ImageUniqueID, imageUniqueIdentifier);
file.Save("path_to_jpg_file");
Nuget package: ExifLibNet.
Here is some code that just needs .NET with PresentationCore and WindowsBase. The underlying technology that WPF uses is WIC (Windows Imaging Component). WIC has full support for image metadata.
EXIF's ImageUniqueID is handled specifically as a Windows Property named System.Image.ImageID
Some other properties such as System.Photo.CameraModel can be seen directly in Windows Explorer detailed views if you add the corresponding column "Camera Model", but not System.Image.ImageID, AFAIK.
// needs PresentationCore & WindowsBase references
var frame = BitmapDecoder.Create(new Uri("test1.jpg", UriKind.RelativeOrAbsolute), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None).Frames[0];
// create encoder, add frame, we need to copy since we want to update metadata
var encoder = BitmapEncoder.Create(frame.Decoder.CodecInfo.ContainerFormat);
var copy = BitmapFrame.Create(frame);
// get frame metadata
var metaData = (BitmapMetadata)copy.Metadata;
// show existing System.Image.ImageID (if any)
Console.WriteLine("ImageUniqueID: " + metaData.GetQuery("System.Image.ImageID"));
// for some reason, we can't use "System.Image.ImageID" to set the meta data
// so use the "Metadata Query Language"
metaData.SetQuery("/app1/ifd/exif/{ushort=42016}", "My Super ID");
// write file back
encoder.Frames.Add(copy);
using (var stream = File.OpenWrite("test1copy.jpg"))
{
encoder.Save(stream);
}
I'm struggling a little with images on the Azure platform under dotnet core and I'm hoping someone can make a sensible suggestion.
Simple enough premise: user uploads image, saved in a database as base64 (about to move to Azure storage blob, but that's irrelevant to this). Later on, site owner comes along and clicks a button to get all these images down in a ZIP file. In the old days of .net framework this was easy. Now I seem to be hitting a set of 'yes, but' comments.
Yes, there's system.drawing.image but you can't use that because it's not in dotnet core (until recently).
Yes, you can use CoreCompat but it doesn't work on Azure because in Web Applications there's no support for GDI+.
Yes, even if I could, I'm developing on a Mac so it won't work locally as far as I can see.
I have tried beta4 of ImageSharp without a lot of success. It's random - sometimes it works, sometimes it just throws OutOfMemoryException.
I have tried SkiaSharp but similar results; sometimes it works, sometimes it spits out random error messages.
I'm not doing anything fancy in terms of processing, no resizing or anything. It should be a case of load file to byte array from Convert.FromBase64String, create Zip file entry, ultimately spit out zip file. The ZIP portion is fine, but I need something decent that can do the image work.
Here's a bit of code:
if(!string.IsNullOrEmpty(del.Headshot))
{
var output=SKImage.FromBitmap(SKBitmap.Decode(Convert.FromBase64String(del.Headshot)));
MemoryStream savedFile=new MemoryStream();
output.Encode(SKEncodedImageFormat.Jpeg, 100).SaveTo(savedFile);
string name=$"{del.FirstName} {del.LastName} - {del.Company}".Trim(Path.GetInvalidFileNameChars()) + "_Headshot.jpg";
ZipArchiveEntry entry=zip.CreateEntry(name);
using(Stream entryStream=entry.Open())
{
entryStream.Write(savedFile.ToArray(), 0, Convert.ToInt32(savedFile.Length));
}
output.Dispose();
savedFile.Dispose();
}
Can anyone give me a sensible suggestion for a library that can handle images, cross-platform and on Azure, before I pull out what little hair remains!
Thanks
EDIT: The first answer is technically correct, I don't need anything else. However, I might have been a bit wrong when I said I wasn't doing any image manipulation. Because it's all base64 without a filename being stored anywhere, I've no idea what sort of file it is. I'm therefore saving each one as JPEG to ensure that I can always output that file type and extension. Users I guess could be uploading JPG / PNG or even GIF.
Technically you do not need any of those other imaging (unless you are doing more that just zipping the content). Convert the base64 to byte array and pass that to the zip file. No need to save to disk just to read it back again for zipping.
//...
if(!string.IsNullOrEmpty(del.Headshot)) {
var imageBytes = Convert.FromBase64String(del.Headshot);
string name = $"{del.FirstName} {del.LastName} - {del.Company}".Trim(Path.GetInvalidFileNameChars()) + "_Headshot.jpg";
ZipArchiveEntry entry = zip.CreateEntry(name);
using(Stream entryStream = entry.Open()) {
entryStream.Write(imageBytes, 0, imageBytes.Length));
}
}
//...
Also using a minor hack for known image types when converted to base64
public static class ImagesUtility {
static IDictionary<string, string> mimeMap =
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "IVBOR", "png" },
{ "/9J/4", "jpg" },
//...add others
};
/// <summary>
/// Extract image file extension from base64 string.
/// </summary>
/// <param name="base64String">base64 string.</param>
/// <returns>file extension from string.</returns>
public static string GetFileExtension(string base64String) {
var data = base64String.Substring(0, 5);
var extension = mimeMap[data.ToUpper()];
return extension;
}
}
You could try to determine the file extension from its prefix
if(!string.IsNullOrEmpty(del.Headshot)) {
var imageBytes = Convert.FromBase64String(del.Headshot);
var ext = ImagesUtility.GetFileExtension(del.Headshot) ?? "jpg";
string name = $"{del.FirstName} {del.LastName} - {del.Company}".Trim(Path.GetInvalidFileNameChars()) + $"_Headshot.{ext}";
ZipArchiveEntry entry = zip.CreateEntry(name);
using(Stream entryStream = entry.Open()) {
entryStream.Write(imageBytes, 0, imageBytes.Length));
}
}
Now ideally, if you are able to control the type of image uploaded, then you should also validate and do what ever image processing when the data is being saved along with any needed metadata (ie content type). That way when extracting it from storage, you can be confident that it is the correct type and size. That saves you having to worry about that later on.
Aspose.Drawing and Aspose.Imaging can handle images and cross-platform running on .NET Core (I'm one of the developers).
I am still very much learning ASP.NET using c# and Webmatrix. I have put together a photography competition site but can't quite find an ideal way of uploading images. I don't see the point of uploading images greater than 1200x900 (projectors maximum resolution) so want to make sure images are small as possible.
I am using tag and checking he image size. If it's too big I am using 'ImageResizer' to resize the image when saving. The only way I know to check the size is to convert the 'HttpPostedFileBase' file into an image using System.Drawing.Image. But when the image is 36Mpixels (it is a photography club) this is taking an age just to read the height and width properties. Can I just load the first x bytes to read the properties or do I have to read the whole image?
The second reason I am converting to an image is to extract the exif data. Again is there an easier and quicker way to read the exif data?
I hope my question makes sense this is all a bit new to me.
simplified code:
HttpPostedFileBase uploadedFile = Request.Files[0];
using (System.Drawing.Image image = System.Drawing.Image.FromStream(uploadedFile.InputStream, true, true))
{
string Exif;
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
try{
Exif = encoding.GetString(image.GetPropertyItem(36867).Value);
}
catch
{
Exif="";
}
if (image.Width <Convert.ToInt32(MaxWidth) && image.Height <Convert.ToInt32(MaxHeight))
{
// SAVE IMAGE AS IS
image.Save(fileSavePath);
// LOAD IMAGE DETAILS WITH EXIF
db.Execute("INSERT INTO CompImages (ImageTitle,CompID,GroupID,ClubID,FileName,UserID,ExifDate) VALUES(#0,#1,#2,#3,#4,#5,#6)",ImageTitle,CompID,GroupID,ClubID,fileName,WebSecurity.CurrentUserId,DateTaken);
}
else
{
// LOAD IMAGE DETAILS WITH EXIF
db.Execute("INSERT INTO CompImages (ImageTitle,CompID,GroupID,ClubID,FileName,UserID,ExifDate) VALUES(#0,#1,#2,#3,#4,#5,#6)",ImageTitle,CompID,GroupID,ClubID,fileName,WebSecurity.CurrentUserId,DateTaken);
// RESIZE IMAGE
ImageResizer.ImageJob iF = new ImageResizer.ImageJob(image, "~/UpImages/"+CompID+"/"+fileName, new ImageResizer.ResizeSettings(
"width="+MaxWidth+";height="+MaxHeight+";format=jpg;mode=max"));
iF.CreateParentDirectory = true; //Auto-create the uploads directory.
iF.Build();
}
}
The only way I know to check the size is to convert the
'HttpPostedFileBase' file into an image using System.Drawing.Image.
You could also checkout the ContentLength property directly:
int uploadedFileSize = uploadedFile.ContentLength;
The second reason I am converting to an image is to extract the exif
data. Again is there an easier and quicker way to read the exif data?
I am not aware of a built-in class in the BCL that would allow you to read EXIF information without loading the image in memory but you could use some third party library like this one: http://www.codeproject.com/Articles/36342/ExifLib-A-Fast-Exif-Data-Extractor-for-NET-2-0
Here is my situation,
I have an ESRI Map Silverlight application that needs to display ShapeFiles that were given to me the client.
The only third party library I've found that will allow you to do this is the ESRI Silverlight API Contrib. The example they give is to use an Open File Dialog to select the shape files and to load them into a FileInfo classes to show. (See example on site's frontpage).
However I run into the issue that since it is a Silverlight app, any attempt to instantiate an instance of a FileInfo object causes the app to crash.
So my question is, is there a way for me to load/display shape files that I have saved locally to the app in Silverlight?
Please let me know if you need me to give more info.
Thanks in advance!
Code:
FileInfo runwayShp = new FileInfo("Layers\\Runway.shp"); //This line breaks, says file access is denied.
FileInfo runwayDbf = new FileInfo("Layers\\Runway.dbf");
ShapeFile shapeFileReader = new ShapeFile();
if (runwayShp != null && runwayDbf != null)
{
shapeFileReader.Read(runwayShp, runwayDbf);
}
GraphicsLayer graphicsLayer = MyMap.Layers["ShapeLayer"] as GraphicsLayer;
foreach (ShapeFileRecord record in shapeFileReader.Records)
{
Graphic graphic = record.ToGraphic();
if (graphic != null)
graphicsLayer.Graphics.Add(graphic);
}
}
I have a silveright app that is doing pretty much the same thing, but what I am doing is uploading the shapefile as a blob to a SQL db on the back end, and then grabbing it from there.
for what you are trying to do, you should look at this codeplex project. I think it will help you out.