I am taking an image by device camera and add an autoAdjustmentFilters. Then I compress it's size and save to document directory. Now problem is when I am sharing this image by airdrop, image becomes larger in size. I am facing this problem when I am sharing the image in JPEG format but not in PDF format. Related code is given below:
let aCGImage = inputImage.CGImage;
self.aCIImage = CIImage(CGImage: aCGImage!)
let Filters = self.aCIImage.autoAdjustmentFilters() as [CIFilter]
for Filter in Filters {
Filter.setValue(self.aCIImage, forKey: kCIInputImageKey)
self.outputImage = Filter.outputImage!
}
let CgImage = self.context.createCGImage(self.outputImage, fromRect: self.outputImage.extent)
self.newUIImage = UIImage(CGImage: CgImage!)
let mydata = UIImageJPEGRepresentation(self.newUIImage, 0.5)
print(">>>>>>>>>>>>,,,,,,,,,\((mydata?.length)! / 1024) kb")
Then I save this image data in document directory. When I save this image in document directory, it's size is 600kb-1500kb, but after sharing it's size is larger than 10000kb. Don't understand what the problem is.
Related
Some context first:
I simply draw a UIImage to a PDFPage by subclassing PDFPage and overriding draw(with box,to context):
override func draw(with box: PDFDisplayBox, to context: CGContext) {
/* Draw image on PDF */
UIGraphicsPushContext(context)
// Change the PDF context to match the UIKit coordinate system.
context.translateBy(x: 0, y: pageBounds.height)
context.scaleBy(x: 1, y: -1)
context.interpolationQuality = .high
// The important line is here: drawing the image
self.myImage.draw(in: CGRect(x: leftMargin, y: topMargin, width: fittedImageSize.width, height: fittedImageSize.height))
}
where self.myImage contains a UIImage. So far so good.
The problem -> if I persist the image to save memory
If I init my CustomPDFPage with the original UIImage from memory --> I get a PDF file with a reasonable size, everything works well
However: if I persist the image using pngData(), then reload it using UIImage(contentsOfFile: url.path) for drawing, my PDF file is suddenly MUCH more heavier in size.
Writing the image to TMP:
let urlToWrite = tmpDir.appendingPathComponent(fileName)
do {
if let tmpData = image.png() {
DLog("TMPDATA SIZE = \(tmpData.count). Image dimensions = \(image.size) with scale = \(image.scale)")
}
try image.pngData()?.write(to: urlToWrite)
self.tmpImgURL = urlToWrite
} catch {
DLog("ERROR: could not write image to \(urlToWrite). Error is \(error)")
}
Reloading the image into memory:
var image = UIImage(contentsOfFile: self.tmpImgURL.path)
--> using that image to draw the PDF increases the PDF size dramatically.
Inspecting the UIImage size, the scale, and the bytes count of the image before writing to file and after reading to file give the exact same values.
So the reason behind this mess is because the user has the possibility to choose to reduce the quality of the image.
In that case, the source UIImage was an image recreated from jpegData (that was used to apply compression).
In short, calling UIImage.pngData() after UIImage.jpegData(...) is not a good idea. Just write directly the jpegData when the image might have been compressed.
I faced a problem, that converting the UIImage to Data and back to UIImage causes the last one to be larger [bytes].
Example (add to your device any photo which is larger than 3MB):
guard let image = UIImage(named: "image_larger_than_3MB"),
let data = image.jpegData(compressionQuality: 1)
else { print("failure"); return }
print(data.count)
guard let imageBack = UIImage(data: data),
let dataBack = imageBack.jpegData(compressionQuality: 1)
else { print("failure 2"); return }
print(dataBack.count)
In my case I receive the following console output:
5304493 // = 5,06 MB // size before conversion
5575422 // = 5,32 MB // size after conversion
So the initial image was of size 5,06 MB. Then I converted it to data with max quality. Then I convert it once again to image, and the size is larger, because it's 5,32 MB. The issue rises with the size of the image.
I understand that the issue is probably connected with compression used in the meantime. Anyway, it surprises me that the size is larger instead of smaller.
Is there any way to maintain the second jpg size same to the initial jpg image when using .jpeg compression..?
Why the size increased (I understand it could decrease)?
func downsizeImage(image: UIImage) -> Data{
var imagePointer = image
let targetDataSize: CGFloat = 256.0 * 256
var imageData = UIImageJPEGRepresentation(image, CGFloat(1.0))!
while (CGFloat(imageData.count) > targetDataSize){
var newProportion = targetDataSize / CGFloat(imageData.count)
print("image data size is \(imageData.count)\n")
imageData = UIImageJPEGRepresentation(imagePointer, CGFloat(newProportion))!
imagePointer = UIImage(data: imageData)!
}
return imageData
}
image data size is 6581432
image data size is 391167
image data size is 394974
image data size is 394915
image data size is 394845
Any clue what my problem is?
The quality parameter to UIImageJPEGRepresentation is not a proportion of image size. Rather it specifies who lossy the JPG compression can be. Quality of zero represents the lossiest and smallest size file the algorithm can generate for your data, but it has no guarantee that its going to be a certain size. The actual size depends on the size and complexity of the image, in terms of color/spatial frequency since thats what the compression algorithm is discarding. If you need to make your image a certain size on disk I suggest you down sample it to a lower resolution and then use a JPG quality setting between 0.4 and 0.6. Lowe values tend to produce a bunch of artifacts, depending on the type of image you are compressing.
EDIT: add code for downsampling extension to UIImage:
extension UIImage {
func resize(to proportion: CGFloat) -> UIImage {
let newSize = CGSize(width: size.width * proportion, height: size.height * proportion)
return UIGraphicsImageRenderer(size: newSize).image { context in
self.draw(in: CGRect(origin: .zero, size: newSize))
}
}
}
I am implementing TopShelf in Apple tvOS. I have downloaded images and assigned to imageURL of TVContentItems. The downloaded images aspect ratio does not fit into the TopShelf Images properly. I have tried to change the size by appending width + height to the image link.
www.mydownloadedimages.com/{width}x{height}
But it didn't work.
Can I do resizing at client end in any other way. In the TVContentItem class I have only have NSURL object. There is no UIImage Object.
Thanks a lot.
Here is Apple's documentation on Image Sizes and shapes
// ~ 2 : 3
// ~ 1 : 1
// ~ 4 : 3
// ~ 16 : 9
// ~ 8 : 3
// ~ 87 : 28
//#property imageShape
//#abstract A TVContentItemImageShape value describing the intended aspect ratio or shape of the image.
//#discussion For Top Shelf purposes: the subset of values which are //valid in this property, for TVContentItems in the topShelfItems property //of the TVTopShelfProvider, depends on the value of the topShelfStyle //property of the TVTopShelfProvider:
TVTopShelfContentStyleInset:
valid: TVContentItemImageShapeExtraWide
TVTopShelfContentStyleSectioned:
valid: TVContentItemImageShapePoster
valid: TVContentItemImageShapeSquare
valid: TVContentItemImageShapeHDTV
When the value of this property is not valid for the Top Shelf style, the system reserves the right to scale the image in any way.
You're right saying that the TVContentItem has no UIImage type property. Since TVContentItem also accepts local file URLs in the imageURL property, a workaround can be:
grabbing the UIImage from internet
creating a new image context with the size of the top shelf image
saving it into the NSCacheDirectory
setting the local image URL as imageURL.
Here are the steps:
Let's create our TVContentItem object:
let identifier = TVContentIdentifier(identifier: "myPicture", container: wrapperID)!
let contentItem = TVContentItem(contentIdentifier: identifier )!
Set the contentItem's imageShape:
contentItem.imageShape = .HDTV
Grab the image from Internet. Actually I did this synchronously, you can also try to use other async methods to get that (NSURLConnection, AFNetworking, etc...):
let data : NSData = NSData(contentsOfURL: NSURL(string: "https://s3-ak.buzzfed.com/static/2014-07/16/9/enhanced/webdr08/edit-14118-1405517808-7.jpg")!)!
Prepare the path where your image will be saved and get your UIImage from the data object:
let filename = "picture-test.jpg"
let paths = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true)
let filepath = paths.first! + "/" + filename
let img : UIImage = UIImage(data: data)!
Assuming you've already set the topShelfStyle property, get the size of the top shelf image with the method TVTopShelfImageSizeForShape. This will be the size of your image context:
let shapeSize : CGSize = TVTopShelfImageSizeForShape(contentItem.imageShape, self.topShelfStyle)
Create your image context of shapeSize size and draw the downloaded image into the context rect. Here you can do all your modifications to the image to adjust it into the desired size. In this example I took a square image from Instagram and I put white letterbox bands on the right and left sides.
UIGraphicsBeginImageContext(shapeSize)
let imageShapeInRect : CGRect = CGRectMake((shapeSize.width-shapeSize.height)/2,0,shapeSize.height,shapeSize.height)
img.drawInRect(imageShapeInRect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
In the end, save this image into your NSCacheDirectory and set the image path as contentItem's imageURL.
UIImageJPEGRepresentation(newImage, 0.8)!.writeToFile(filepath, atomically: true)
contentItem.imageURL = NSURL(fileURLWithPath: filepath)
Complete your TVContentItem with other details (like title, internal link, etc...), run the top shelf extension... et voilĂ !
I would like to add to Nicola Giancecchi's answer. The Top Shelf extension will run on a non main thread so using methods like UIImageJPEGRepresentation or UIImagePNGRepresentation will sometimes stop the Top Shelf thread printing this in the console:
Program ended with exit code: 0
To fix this you can wrap your code like this:
DispatchQueue.main.sync {
UIImageJPEGRepresentation(newImage, 0.8)!.writeToFile(filepath, atomically: true)
let imageURL = NSURL(fileURLWithPath: filepath)
if #available(tvOSApplicationExtension 11.0, *) {
contentItem.setImageURL(imageURL, forTraits: .userInterfaceStyleLight)
contentItem.setImageURL(imageURL, forTraits: .userInterfaceStyleDark)
} else {
contentItem.imageURL = imageURL
}
}
Currently I am working on an NSImageView, on which the user drags and drops an image, and the images gets saved to the disk. I am able to save png and jpeg images, but while saving GIF images, all that is saved is a single frame from the gif. The imageView is able to display the whole animated gif though.
My current implementation to save image from NSImageView to disk is:
let cgRef = image.CGImageForProposedRect(nil, context: nil, hints: nil)
let newRep = NSBitmapImageRep(CGImage: cgRef!)
newRep.size = image.size
let type = getBitmapImageFileType(imageName.lowercaseString) // getBitmapImageFileType: returns NSBitmapImageFileType
let properties = type == .NSGIFFileType ? [NSImageLoopCount: 0] : Dictionary<String, AnyObject>()
let data: NSData = newRep.representationUsingType(type, properties: properties)!
data.writeToFile(link, atomically: true)
How should I modify this code to be able to save all the frames of GIF to the disk.