I'm currently trying to save a jpeg representation of a UIImage with additional custom metadata (e.g. Thermal temperature statistics etc.). These don't fit within the apple predefined keys (https://developer.apple.com/documentation/imageio/cgimageproperties), so solutions I've found don't apply to my scenario.
I've tried saving the metadata with the image as a dictionary of keys and values, but the image is saved without the additional metadata.
func saveImage(imageToSave: UIImage, metadata: NSMutableDictionary) {
if let data: Data = imageToSave.jpegData(compressionQuality: ThermalImageView.JPEG_COMPRESSION) {
let fileName = self.buildFileName();
let source = CGImageSourceCreateWithData(data as CFData, nil)!;
let uniformTypeIdentifier = CGImageSourceGetType(source)!;
let destination = CGImageDestinationCreateWithURL(fileName as CFURL, uniformTypeIdentifier, 1, nil)!;
CGImageDestinationAddImageFromSource(destination, source, 0, metadata);
CGImageDestinationFinalize(destination);
}
}
When I try to read these values back with ExifTool (exiftool -j filename.jpg), the metadata is nowhere to be found. I expected this to happen as Apple seems to restrict what keys you can add to your metadata. So, is there a way to do this or should I go another route?
Thanks!
Edit: I think I may be barking up the wrong tree here. It seems like what I actually want to do is modify the header with additional metadata.
Photos framework is a way to go when dealing with metadata of assets. Moreover, PHAsset is the object you'd like to tinker with. https://developer.apple.com/documentation/photokit/phasset
One related question is: https://forums.developer.apple.com/thread/60664
If you still want to have it in your current way, perhaps try this? https://github.com/Nikita2k/SimpleExif
Related
I'm writing an iOS Swift app to fetch metadata from DJI drone images. I'm trying to access the Xmp.drone-dji.X metadata. The iOS/Swift CGImageSource and CGImageMetadata libraries/classes get almost all of the metadata out of the image but not the Xmp.drone-dji. When I get a list of tags, those tag/values are not listed. I know the tags/data are in the images because I've examined the images using exif, exiv2, etc.
Any suggestions?
Here is the code I'm using so far:
result.itemProvider.loadDataRepresentation(forTypeIdentifier: UTType.image.identifier)
{ data, err in
if let data = data {
let src = CGImageSourceCreateWithData(data as CFData,nil)!
let md = CGImageSourceCopyPropertiesAtIndex(src,0,nil) as! NSDictionary
let md2 = CGImageSourceCopyMetadataAtIndex(src,0,nil)
}
Thanks,
Bobby
So, after a lot of searching, trial and error, I have found an answer.
I was not able to get any of the CGImage swift libraries to extract this info for me.
Adobe has a c++ library that parses xmp/xml data out of images and it purports to support iOS. I didnt want the hassle of building c++ on iOS, importing that into Xcode and then dealing with the fact that thrown errors do not propagate well from c++/objectiveC to Swift.
So, at a high level, I did the following:
get the bytes of the raw image as CFData or Data then cast to a String
then use String.range() to find beginning of XML/XMP data in image
searching for substring <?xpacket begin
use String.range() to find end of XML/XMP data in image
using substring <?xpacket end.*?>
Extract the XML document out of image data String
Use Swift XMLParser class to parse the XML and then copying attributes and
elements as necessary. I just simply added what I wanted to already
existing Exif NSdictionary returned by CGImage classes.
Happy to answer questions on this approach. My code will eventually be uploaded to GitHub under OpenAthenaIOS project.
Bobby
I am using SwiftUI and Realm.
I want to save an Image to the database. The image is a photo taken from the device's camera and is not stored in the gallery.
It's my understanding that I need to convert the image to NSData, but I can only find syntax that works with UIImage.
I tried
let uiImage: UIImage = image.asUIImage()
but get this error:
Value of type 'Image?' has no member 'asUIImage'
What am I doing wrong here, how can I have an Image to Realm local database?
EDIT: Thanks for the suggested "possible duplicate", but no: the duplicate (which I have already seen prior to making my post) is for UIImage (UIKit). I am using Image (SwiftUI).
The struct Image is only a building block for the UI in Swift UI and it is not an object that represents the literal image, but rather something that displays some image.
The common approach, is to see how you create Image - where do you get the actual image from - and to use the source, the image itself to save it.
Just as side note, I wanted to mention that storing data blobs in Realm database can be extremely slow and more commonly used and fast approach is to write files to disk and to store only the names of those files in the database.
Elaborating on this approach, you can:
create a folder to store your images in Library path in user domain mask
You can read about iOS Sandbox file system and where you can store stuff at Apple File System Programming Guide.
For our purposes, it will suffice to this method.
let imagesFolderUrl = try! FileManager.default.url(for: . applicationSupportDirectory, in: .userDomainMask)
.appendingPathComponent("images_database")
You should check if this directory exists and create it if it doesn't - there's plenty of information about this online.
Then, when you have an image Data, you give it a name, you create a URL that will point to where it will be stored and then you write it.
let imageData: Data
let imageName = "some new name for this particular image - maybe image id or something"
let imageUrl = imagesFolderUrl.appendingPathComponent(imageName)
imageData.write(to: url) // Very slow operation that you should perform in background and not on UI thread
Then, you store the name of the image in the Realm database
Then, when you pull a record from Realm database and see the name of the image, you can construct the url again and read a Data from it
let record: RealmRecord
let imageName = record.imageName
let url = imagesFolder.appendingPathComponent(imageName)
let data = Data(url: imageName)
That's overly simplifying it.
In the app I work on, I am being tasked with a requirement to present the user their device photos using a PHPickerViewController.
Once selected, I need access to:
The original image data
The file name of the image
The creation date of the image
This is simple to do with a PHAsset if I had full access to the user's photos - I could use the PHPickerViewController result value which includes photo identifiers to query PhotoKit for the PHAssets. However, there is no guarantee that I have full access. Additionally if the user has granted no access or limited access, and the limited selection does not include the photos being selected in the PHPickerViewController, I won't be able to query for them. (I tried this, and as expected the result of the query is nil, which makes sense).
The UIImage that I can obtain is only a proxy of the original image data (missing things like exif data, etc), so that is not sufficient for the user case my app has. Assuming there was an item provider for data, I'd still need to get information such as file name and creation date as well though.
So, is there a way to obtain this information?
The UIImage that I can obtain is only a proxy of the original image data (missing things like exif data, etc), so that is not sufficient for the user case my app has
Go back to the NSItemProvider and ask it for the data representation of the image. Now extract the metadata in the usual way.
// prov is the item provider
prov.loadDataRepresentation(forTypeIdentifier: UTType.image.identifier) { data, err in
if let data = data {
let src = CGImageSourceCreateWithData(data as CFData, nil)!
let d = CGImageSourceCopyPropertiesAtIndex(src,0,nil) as! [AnyHashable:Any]
print("metadata", d)
}
}
I'm currently storing my images directly in core data and experiencing issues when having to retrieve a high quality images.
I was advised to store the images in the document directory and save a path in core-data as to not store the actual images there but simply the address of where I can go and find it.
Can someone push me in the right direction? Can't seem to find the appropriate guidance?
I was able to find the answers to all my questions in this youtube video -
https://www.youtube.com/watch?v=6vk4UrJR8WM
This is the github with the file that has instructions as to how to complete this - https://github.com/TDAbboud/WeightLogger-Images
Try Hanake cache: https://github.com/Haneke/Haneke
It worked really well for me personally with the same problem. It makes a sometimes painful task pretty easy.
Save an image in Document directory is easy this code is not optimized is just for give you a hint( you should manage errors, optionals etc). Suppose that you already have a valid UIImage instance called image and a valid image name String called imageName
let data = UIImagePNGRepresentation(image)
// Unwrapping the data optional
if let data = data {
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let path = (paths.first! as! NSString).stringByAppendingPathComponent(imageName)
data.writeToFile(path, options: [])
//Save the path into your managed object
}
I am putting together a program that reads the sensors within a cell phone and saves the sensor data to a core-data SQLite model, with each set of readings pertaining to a particular session
The program provides the user with the option to email a .csv file of a particular session.
Having never done this before, I approached the issue by initializing a delegate and context, and searching the core data for entities that pertain to a specified session. The entities that satisfy the session attribute then have their data fields (gps, mag, accel, gyro) read and put into a string. Then the string is appended to an array. All done in swift.
After the entities are searched and the array is created, I attempt to create a csv file for attachment to an email. The file is attached successfully, but my encoding technique is presenting additional data prepended and appended to the file.
I want to save a file on the phone and email a copy to the user.
Here is what I have to change the Array to NSArray before converting again to NSData:
let paths: NSArray = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true);
let path = paths[0].stringByAppendingPathComponent("SessionData.csv")
if !NSFileManager.defaultManager().fileExistsAtPath(path)
{
NSFileManager.defaultManager().createFileAtPath(path, contents: nil, attributes: nil)
}
else
{
NSFileManager.defaultManager().createFileAtPath(path, contents: nil, attributes: nil)
}
var handle: NSFileHandle = NSFileHandle(forWritingAtPath: path)
handle.truncateFileAtOffset(handle.seekToEndOfFile())
var arrayToWriteNS = (arrayToWrite as NSArray)
var dataNS: NSData = NSKeyedArchiver.archivedDataWithRootObject(arrayToWrite as NSArray)
handle.writeData(dataNS)
mc.setSubject(emailTitle)
mc.addAttachmentData(dataNS, mimeType: "text/csv", fileName: "SessionData.csv")
Here is the prepended and appended data:
bplist00‘()T$topX$objectsX$versionY$archiver—TrootĨ
!U$null“
V$classZNS.objectsÄ©ÄÄÄÄÄÄÄÄ Ä
"My Data"
“"#$'X$classesZ$classname¢%&WNSArrayXNSObjectWNSArray܆_NSKeyedArchiver(25:<IOT[fhrtvxz|~ÄÇÑ·Ø}KÁµÉQV_jmu~Üã*ù
In a large data session with 28,000 entities there may be ~750 lines of prepended data.
Any help that you can provide would be appreciated.
I'm new to iOS, Obj-C, and swift, thus I'm positive there is a better way to do this, I just haven't discovered a better method yet.
Thank you.
UPDATE: Ended up just using the NSString data encoding and writing to my file in increments:
handle.truncateFileAtOffset(handle.seekToEndOfFile())
var stringToWriteNS = (stringToWrite as NSString).dataUsingEncoding(NSUTF8StringEncoding)
handle.writeData(stringToWriteNS!)
You do not want NSKeyedArchiver, it is for archiving and restoring classes.
You need to go through the array and create a text representation of each item in a format you what to present to the user.
A quick search of CocoaPods reveals several projects that may fit you needs to generate csv format data.
This one might be what you need.
csv is fairly simple so it would be reasonable to format your data to csv by writing your own code.