Reduce base64 encoded string size - ios

I am using the following method to save the PNG image to UserDefaults:
func convertImageToBase64String (img: UIImage) -> String {
return img.pngData()?.base64EncodedString() ?? ""
}
The issues are that this data is using significantly Documents & Data storage:
And for some reason even if this data is removed from UserDefaults, this amount do not change.
I would like to fix this issues, but need to use PNG images as transparent background is important.

After some research, I found out PNG files were not using significant storage. The issue was that original images were being stored in temporary data. I managed to fix this issue following this suggestion: https://stackoverflow.com/a/37043842/14880207
I followed Ramesh Sanghar suggestion as well and stored images in the way he suggested.

Related

iOS, Swift, Image Metadata, XMP, DJI Drones

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

UIImagePNGRepresentation loses its transparency

I am working on an app that connects multiple devices using the networking protocol UPnP.
Among others, I can exchange images and they are files stored inside a folder in my project folder.
For sending the images I am given predefined methods.
if let imageRequested = UIImage(named:"logo.png"){
let fileType = "image/png"
let pngRepresentation = [UInt8](UIImagePNGRepresentation(imageRequested)!)
let fileSize = UInt32(pngRepresentation.count)
writeBegin!(aWriterData, fileSize, fileType)
aWriteResource!(aWriterData, pngRepresentation, fileSize)
aWriteEnd!(aWriterData)
}
However, the receiver gets the image with black background when it is transparent. I know It is not a receiver issue because there is an Android version that sends the same images and they are received with transparency.
any ideas why?
UIImageJPEGRepresentation will convert the resulting image into a JPEG, which doesn't support transparency. Are you sure that you use UIImagePNGRepresentation, not UIImageJPEGRepresentation?
If you use UIImagePNGRepresentation - it's strange, it should work fine, could you provide more details?

Detecting that iOS image data is HEIF or HEIC

My server doesn't support the HEIF format. So I need to transform it to JPEG before uploading from my app.
I do this:
UIImage *image = [UIImage imageWithData:imageData];
NSData *data=UIImageJPEGRepresentation(image, 1.0);
But how can I know that the data is HEIF (or HEIC) ? I can look at a file:
([filePath hasSuffix:#".HEIC"] || [filePath hasSuffix:#".heic"])
But I don't think it's a good answer. Is there any other solution?
Both existing answers have good recommendations, but to attempt to tell the whole story...
UIImage doesn't represent an image file or even binary data in an image-file format. A UIImage is best thought of as an abstract representation of the displayable image encoded in that data — that is, a UIImage is the result of the decoding process. By the time you have a UIImage object, it doesn't care what file format it came from.
So, as #Ladislav's answer notes, if you have a UIImage already and you just want to get data in a particular image file format, call one of the convenience functions for getting a UIImage into a file-formatted data. As its name might suggest, UIImageJPEGRepresentation returns data appropriate for writing to a JPEG file.
If you already have a UIImage, UIImageJPEGRepresentation is probably your best bet, since you can use it regardless of the original image format.
As #ScottCorscadden implies, if you don't have a UIImage (yet) because you're working at a lower level such that you have access to the original file data, then you'll need to inspect that data to divine its format, or ask whatever source you got the data from for metadata describing its format.
If you want to inspect the data itself, you're best off reading up on the HIEF format standards. See nokiatech, MPEG group, or wikipedia.
There's a lot going on in the HEIF container format and the possible kinds of media that can be stored within, so deciding if you have not just a HEIF file, but an HEIF/HEVC file compatible with this-or-that viewer could be tricky. Since you're talking about excluding things your server doesn't support, it might be easier to code from the perspective of including only the things that your server does support. That is, if you have data with no metadata, look for something like the JPEG magic number 0xffd8ff, and use that to exclude anything that isn't JPEG.
Better, though, might be to look for metadata. If you're picking images from the Photos library with PHImageManager.requestImageData(for:options:resultHandler:), the second parameter to your result handler is the Uniform Type Identifier for the image data: for HEIF and HEIC files, public.heif, public.heif-standard, and public.heic have been spotted in the wild.
(Again, though, if you're looking for "images my sever doesn't support", you're better off checking for the formats your server does support and rejecting anything not on that list, rather than trying to identify all the possible unsupported formats.)
When you are sending to your server you are most likely decoding the UIImage and sending it as Data so just do
let data = UIImageJPEGRepresentation(image, 0.9)
Just decide what quality works best for you, here it is 0.9
A bit late to the party, but other than checking the extension (after the last dot), you can also check for the "magic number" aka file signature. Byte 5 to 8 should give you the constant "ftyp". The following 4 bytes would be the major brand, which I believe is one of "mif1", "heic" and "heix".
For example, the first 12 bytes of a .heic image would be:
00 00 00 18 66 74 79 70 6d 69 66 31
which, after removing 0s and trim the result, literally decoded to ftypmif1.
Well, you could look at magic bytes - JPEG and PNG certainly are known, and I seem to see some references that HEIF (.heic) starts with a NUL byte. If you're using any of the PHImageManager methods like requestImageDataForAsset:options:resultHandler, that resultHandler will be passed a NSString * _Nullable dataUTI reference. There's a decent WWDC video/slides on this (possibly here) that suggest if the UTI is not kUTTypeJPEG you convert it (and the slides have some lower-level sample code in swift to do it that preserve orientation too).
I should also mention, if you have control at your app layer and all uploads come from there, do all this there.
If you're using Photos framework and are importing images from photo library, there's a solution that was mentioned briefly during WWDC17. First, import core services:
import MobileCoreServices
Then, when you request the image, check the UTType that is returned as a second parameter to your block:
// asset: PHAsset
PHImageManager.default().requestImageData(for: asset, options: nil) { imageData, dataUTI, orientation, info in
guard let dataUTI = dataUTI else { return }
if !(UTTypeConformsTo(dataUTI as CFString, kUTTypeJPEG) || UTTypeConformsTo(dataUTI as CFString, kUTTypePNG)) {
// imageData is neither JPG not PNG, possibly subject for transcoding
}
}
Other UTTypes can be found here

Delete File After contentsOfFile Load

I am loading an array of UIImage from the iOS Documents directory:
var images = [UIImage]()
for fileName in fileNames {
images.append(UIImage(contentsOfFile: "\(imagesPath)/\(fileName).png")!)
}
I'm going to continue using this array but I don't need the files anymore, so I go ahead and delete them:
for fileName in fileNames {
do {
try NSFileManager.defaultManager().removeItemAtPath("\(imagesPath)/\(fileName).png")
} catch {
print("Error")
}
}
When I do this, my array of UIImage is now invalid and gives me errors while trying to access them. Shouldn't this be in memory and not related to the files on disk?
I tried using the ".copy()" command on the images when I load them but that made no difference.
I have confirmed that the delete is the issue above because if I comment out that line the app works great with no errors. I only get errors accessing the array after I delete the files from disk.
Is there a way to sever this connection?
Edit: Per the correct answer from #Wain, the code works fine if I change it to:
var images = [UIImage]()
for fileName in fileNames {
let imgData = NSFileManager.defaultManager().contentsAtPath("\(imagesPath)/\(fileName).png")!
images.append(UIImage(data: imgData)!)
}
Doing this doesn't keep the link back to the file on disk.
The images haven't been displayed so the data hasn't fully been loaded yet, only enough to know the image details and size has been loaded. This is for memory efficiency. depending on the image format and usage different parts of data may be loaded at multiple different times.
If you load the file into NSData, ensuring that it isn't memory mapped, and create the image from that then the data and image should be unlinked from the underlying file. This is less memory efficient and it would be better to keep your current code and delete the files when you know you're finished with the images all together.

How can I get raw data of image from UIPasteboard if it's copied by another app (such as photos, safari, etc..)

Is there a way to get raw data of image from UIPasteboard instead of UIImage if it's copied by another app such as photos or mobile safari?
I'm currently facing wired differences between IOS 6.0 and IOS 6.1(7.0 also)
In IOS 6.0, UIPasteboard's item of the copied image by photos or mobile safari contains raw data of the image.
But In IOS 6.1 and above, it contains UIImage instead of raw data.
In IOS 6.0, copied item of UIPasteboard is below
Printing description of array:
<__NSArrayM 0x8a804c0>(
{
"com.compuserve.gif" = <47494638 39614002 ...... 3b3a2000 3b>;
"public.url" = "url of the image....";
}
)
In IOS 6.1 and above, it contains UIImage instead of raw data.
Printing description of array:
<__NSArrayM 0xa25b7b0>(
{
"com.compuserve.gif" = "<UIImage: 0x9429570>";
"public.url" = "url of the image...";
}
)
If that image format is PNG or JPEG, it's not that bad.
(I still have to compress again if it's JEPG though.)
But when I try to paste animated gif image, it becomes more complicated.
I don't know even it's passible to create animated gif image from normal UIImage.
I can download again from original url, but downloading data that I already have seems not good solution I think.
And also, if it's copied from photos app, there's no such url. (there's some mysterious uri named "com.apple.mobileslideshow.asset-object-id-uri" that is undocumented instead of url)
There seems a workaround, because when I try to do exactly same action between photos and email app, It works properly
Any suggestions?
Well now, I figured it out myself.
You can simply get raw (binary)data of the image form general pasteboard by sending
dataForPasteboardType:(NSSting*)PasteboardType message to general pasteboard, if it's copied from Apple's built-in Mobile Safari or Photos App. (#"com.compuserve.gif" for the pasteboard type in my case)
I myself feel a bit foolish for not having checked all the passible methods sooner. :(
My confusion comes from items property of the UIPasteboard.
I thought that items are containing all of data of current pasteboard. So I try to save that array from pasteboard and want to use it later, but I were totally wrong.
As documented in UIPasteboard Class Reference, the items property contains dictionary with key being the representation type and the "value" the object associated with that type.
At this point of time, The "value" refers really "value" of the representation, not the data of that type. This meaning of the "value" is the same as the value of thesetValue: forPasteboardType: method.
On the other words, you cannot retrieve raw(binary) data of the image from items property, even if you set the image to the pasteboard by sending setData: forPasteboardType: message.(I tested it on IOS 7)
In addition, raw data of the image from items property in IOS 6.0 seems a bug of that OS version. (This may not true, it's just my opinion)
You can get NSData from UIPasteboard if you specify right PasteboardType:
NSData* pasteData = [[UIPasteboard generalPasteboard] dataForPasteboardType:(NSString*)kUTTypeJPEG];
Do not forget to import
<MobileCoreServices/MobileCoreServices.h>
The UIPasteBoard will contain whatever is placed in it. It's up to the app that is copying to the paste board to put the contents in the proper format. The app can place items as raw binary data or as objects such as UIImage in the paste board.
If you're getting something different between iOS versions, you're probably using different versions of the app or it's simply copying things differently.
You're right that you can't represent an animated GIF in a UIImage because a UIImage only contains a single image. Perhaps the app is just copying the first frame's bitmap data in that case?
You can convert a UIImage to raw JPEG data using UIImageJPEGRepresentation.

Resources