Стоит уточнить, что с языком программирования Swift и со всеми его компонентами я знаком уже три дня!
Три дня пытаюсь решить эту проблему, ни одного решения не нашел, перерыл много сайтов! Мне нужно убедиться, что изображения при загрузке на сервер веселились не более 150 кб, но в то же время, чтобы качество было хорошим.
Во время поиска я нашел алгоритм, на сайтах и на GitHub они писали, что это была копия сжатия изображения в мессенджере WhatsApp, но на iphone 8 он не очень хорошо сжимает изображение. Если вы приведете пример, сжатие сфотографированного изображения на iphone 8 при передаче в мессенджер WhatsApp сжимается до 100 кб., При этом качество изображения очень хорошее, а длина и ширина больше 1000 пикселей. И если вы используете этот алгоритм, то при длине: 800px и ширине: 600px и степени сжатия 0,01 изображение весит более 200 кб. и качество ужасное.
Вот алгоритм:
extension UIImage {
func compressImage() -> UIImage? {
// Reducing file size to a 10th
var actualHeight: CGFloat = self.size.height
var actualWidth: CGFloat = self.size.width
let maxHeight: CGFloat = 800.0
let maxWidth: CGFloat = 600.0
var imgRatio: CGFloat = actualWidth/actualHeight
let maxRatio: CGFloat = maxWidth/maxHeight
var compressionQuality: CGFloat = 0.01
if actualHeight > maxHeight || actualWidth > maxWidth {
if imgRatio < maxRatio {
//adjust width according to maxHeight
imgRatio = maxHeight / actualHeight
actualWidth = imgRatio * actualWidth
actualHeight = maxHeight
} else if imgRatio > maxRatio {
//adjust height according to maxWidth
imgRatio = maxWidth / actualWidth
actualHeight = imgRatio * actualHeight
actualWidth = maxWidth
} else {
actualHeight = maxHeight
actualWidth = maxWidth
//compressionQuality = 1
}
}
let rect = CGRect(x: 0.0, y: 0.0, width: actualWidth, height: actualHeight)
UIGraphicsBeginImageContext(rect.size)
self.draw(in: rect)
guard let img = UIGraphicsGetImageFromCurrentImageContext() else {
return nil
}
UIGraphicsEndImageContext()
guard let imageData = UIImageJPEGRepresentation(img, compressionQuality) else {
return nil
}
return UIImage(data: imageData)
}
}
Поскольку он не работал с этим алгоритмом, я начал искать библиотеку сжатия изображений для Swift, но, к сожалению, я ничего не нашел! Затем я попытался использовать алгоритм сжатия LZFSE, но, как я понял, без декомпрессора на другом устройстве (предположим, с ОС Android) изображение не будет отображаться, кроме того, он пожаловался на nil-данные в одной из строк кода. ,
Вот код:
public enum CompressionAlgorithm {
case lz4 // speed is critical
case lz4a // space is critical
case zlib // reasonable speed and space
case lzfse // better speed and space
}
private enum CompressionOperation {
case compression, decompression
}
private func perform(_ operation: CompressionOperation,
on input: Data,
using algorithm: CompressionAlgorithm,
workingBufferSize: Int = 2000) -> Data? {
var output = Data()
// set the algorithm
let streamAlgorithm: compression_algorithm
switch algorithm {
case .lz4: streamAlgorithm = COMPRESSION_LZ4
case .lz4a: streamAlgorithm = COMPRESSION_LZMA
case .zlib: streamAlgorithm = COMPRESSION_ZLIB
case .lzfse: streamAlgorithm = COMPRESSION_LZFSE
}
// set the stream operation, and flags
let streamOperation: compression_stream_operation
let flags: Int32
switch operation {
case .compression:
streamOperation = COMPRESSION_STREAM_ENCODE
flags = Int32(COMPRESSION_STREAM_FINALIZE.rawValue)
case .decompression:
streamOperation = COMPRESSION_STREAM_DECODE
flags = 0
}
// create a stream
var streamPointer = UnsafeMutablePointer<compression_stream>
.allocate(capacity: 1)
defer {
streamPointer.deallocate(capacity: 1)
}
// initialize the stream
var stream = streamPointer.pointee
var status = compression_stream_init(&stream, streamOperation, streamAlgorithm)
guard status != COMPRESSION_STATUS_ERROR else {
return nil
}
defer {
compression_stream_destroy(&stream)
}
// set up a destination buffer
let dstSize = workingBufferSize
let dstPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: dstSize)
defer {
dstPointer.deallocate(capacity: dstSize)
}
// process the input
return input.withUnsafeBytes { (srcPointer: UnsafePointer<UInt8>) in
stream.src_ptr = srcPointer
stream.src_size = input.count
stream.dst_ptr = dstPointer
stream.dst_size = dstSize
while status == COMPRESSION_STATUS_OK {
// process the stream
status = compression_stream_process(&stream, flags)
// collect bytes from the stream and reset
switch status {
case COMPRESSION_STATUS_OK:
output.append(dstPointer, count: dstSize)
stream.dst_ptr = dstPointer
stream.dst_size = dstSize
case COMPRESSION_STATUS_ERROR:
return nil
case COMPRESSION_STATUS_END:
output.append(dstPointer, count: stream.dst_ptr - dstPointer)
default:
fatalError()
}
}
return output
}
}
// Compressed keeps the compressed data and the algorithm
// together as one unit, so you never forget how the data was
// compressed.
public struct Compressed {
public let data: Data
public let algorithm: CompressionAlgorithm
public init(data: Data, algorithm: CompressionAlgorithm) {
self.data = data
self.algorithm = algorithm
}
// Compress the input with the specified algorithm. Returns nil if it fails.
public static func compress(input: Data,
with algorithm: CompressionAlgorithm) -> Compressed? {
guard let data = perform(.compression, on: input, using: algorithm) else {
return nil
}
return Compressed(data: data, algorithm: algorithm)
}
// Factory method to return uncompressed data. Returns nil if it cannot be decompressed.
public func makeDecompressed() -> Data? {
return perform(.decompression, on: data, using: algorithm)
}
}
// For discoverability, add a compressed method to Data
extension Data {
// Factory method to make compressed data or nil if it fails.
public func makeCompressed(with algorithm: CompressionAlgorithm) -> Compressed? {
return Compressed.compress(input: self, with: algorithm)
}
}
Здесь, в этой строке, после применения этого алгоритма:
UIImage(data: imageCompressData)//к переменной imageCompressData был применен код сверху!
Далее я случайно наткнулся на этот код:
func resizeImageUsingVImage(image:UIImage, size:CGSize) -> UIImage? {
let cgImage = image.cgImage!
var format = vImage_CGImageFormat(bitsPerComponent: 8, bitsPerPixel: 32, colorSpace: nil, bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.first.rawValue), version: 0, decode: nil, renderingIntent: CGColorRenderingIntent.defaultIntent)
var sourceBuffer = vImage_Buffer()
defer {
free(sourceBuffer.data)
}
var error = vImageBuffer_InitWithCGImage(&sourceBuffer, &format, nil, cgImage, numericCast(kvImageNoFlags))
guard error == kvImageNoError else { return nil }
// create a destination buffer
let scale = image.scale
let destWidth = Int(size.width)
let destHeight = Int(size.height)
let bytesPerPixel = image.cgImage!.bitsPerPixel/8
let destBytesPerRow = destWidth * bytesPerPixel
let destData = UnsafeMutablePointer<UInt8>.allocate(capacity: destHeight * destBytesPerRow)
defer {
destData.deallocate(capacity: destHeight * destBytesPerRow)
}
var destBuffer = vImage_Buffer(data: destData, height: vImagePixelCount(destHeight), width: vImagePixelCount(destWidth), rowBytes: destBytesPerRow)
// scale the image
error = vImageScale_ARGB8888(&sourceBuffer, &destBuffer, nil, numericCast(kvImageHighQualityResampling))
guard error == kvImageNoError else { return nil }
// create a CGImage from vImage_Buffer
var destCGImage = vImageCreateCGImageFromBuffer(&destBuffer, &format, nil, nil, numericCast(kvImageNoFlags), &error)?.takeRetainedValue()
guard error == kvImageNoError else { return nil }
// create a UIImage
let resizedImage = destCGImage.flatMap { UIImage(cgImage: $0, scale: 0.0, orientation: image.imageOrientation) }
destCGImage = nil
return resizedImage
}
Чуть более подробно рассмотрев код и изучив документацию для vImage и библиотеки Accelerate, я понял, что эта очень мощная библиотека обработки изображений, если у меня хватит знаний, может быть, я смогу создать хороший компрессор изображений, но мой знаний недостаточно, поэтому вся надежда на Вас.