Swift 来自 collectionView 的 CIfilter select

Swift CIfilter select from a collectionView

我正在开发一个应用程序,它可以让用户 select 照片应用滤镜并保存。 基本上是 instagram 应用程序相机的副本,用户 select 来自库的照片,应用滤镜,然后 post 它。

该应用程序运行良好,我唯一的问题是在使用过滤后的图像加载 collectionView 时,过程有点慢。 我已经阅读了一些内容(我是 swift 的新手,这是我第一次使用核心图像)并且我尝试应用我学到的所有建议。 但是我相信该应用程序仍然 运行 慢。

所以这是我所做的。

我创建了一个带有过滤器的模型:

public protocol Filter {
 // reference to the core image filter
var filter: CIFilter { get }
 // output of the filter.
var outputImage: CIImage? { get }
}

 // common to all filters.
 extension Filter {
   public var outputImage: CIImage? { return self.filter.outputImage }
}

//Bloom
public class Bloom: Filter {

public let filter: CIFilter

public init(inputImage: CIImage, inputRadius: CGFloat = 10.0, 
 inputIntensity: CGFloat = 1.0) {
    let parameters:[String : Any] = [
        "inputImage":inputImage,
        "inputRadius":inputRadius,
        "inputIntensity":inputIntensity        ]
    guard let filter = CIFilter(name:"CIBloom", withInputParameters: parameters) else { fatalError() }
    self.filter = filter
  }
}


// Box Blur
public class BoxBlur: Filter {

public let filter: CIFilter

public init(inputImage: CIImage, inputRadius: CGFloat = 10.0) {
    let parameters:[String : Any] = [
        "inputImage":inputImage,
        "inputRadius":inputRadius        ]
    guard let filter = CIFilter(name:"CIBoxBlur", withInputParameters: parameters) else { fatalError() }
    self.filter = filter
  }
}
...........

等等我需要的所有过滤器。

我创建了一个 class 来创建一个包含所有过滤图像的 UIImage 数组:

class filteredImages {
static var filterToApply: Filter!
static var filterNames = [
    "Original","Sepia","Poster",....
]

static var filteredImages = [UIImage]()
static var filteredImage: UIImage!

static func createImageArray(inputImage: CIImage, onSuccess: @escaping () -> () ) {
    filteredImages.removeAll()
    for filter in filterNames {
        switch filter {
        case "Original":
            filteredImage = UIImage(ciImage:inputImage)
            filteredImages.append(filteredImage)
        case "Sepia":
            filterToApply = SepiaTone(inputImage: inputImage, inputIntensity: 0.8)
            filteredImage = UIImage(ciImage: filterToApply.outputImage!)
            filteredImages.append(filteredImage)
............
      }
      onSuccess()
     }
   }
}

最后在我的控制器中:

var filteredImagesArray = [UIImage]()
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(true)
    let inputImage = CIImage(image: (originalImage.image?.resized(toWidth: 120))!)!
    filteredImages.createImageArray(inputImage: inputImage) {
        self.filteredImagesArray = filteredImages.filteredImages
    }
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FilterCell", for: indexPath) as! FilterCell

        cell.filteredImage.image = filteredImagesArray[indexPath.row]
        cell.filterName.text = filteredImages.filterNames[indexPath.row]
        cell.layer.borderWidth = 0.7
        cell.layer.borderColor = UIColor.white.cgColor
    return cell
}

我还有一个缩小"original"图像的扩展:

extension UIImage {
func resized(toWidth width: CGFloat) -> UIImage? {
    let canvasSize = CGSize(width: width, height: CGFloat(ceil(width/size.width * size.height)))
    UIGraphicsBeginImageContextWithOptions(canvasSize, false, scale)
    defer { UIGraphicsEndImageContext() }
    draw(in: CGRect(origin: .zero, size: canvasSize))
    return UIGraphicsGetImageFromCurrentImageContext()
  }
}

有没有办法让它更快? 我错过了什么吗?

谢谢。

------------根据拉迪斯拉夫的请求更新

这是我用来从库中获取图像的代码:

  enum Section: Int {
    case allPhotos = 0
    case smartAlbums
    case userCollections

    static let count = 3
}
var allPhotos: PHFetchResult<PHAsset>!
var allPhotosFromAlbum = PHFetchResult<PHAsset>()
var smartAlbums: [PHAssetCollection] = []
var userCollections: PHFetchResult<PHAssetCollection>!
let subtypes:[PHAssetCollectionSubtype] = [
    .smartAlbumFavorites,
    .smartAlbumPanoramas,
    .smartAlbumScreenshots,
    .smartAlbumSelfPortraits,
    .smartAlbumVideos,
    .smartAlbumRecentlyAdded,
    .smartAlbumSelfPortraits
]
 override func viewDidLoad() {
    super.viewDidLoad()
    // Create a PHFetchResult object for each section in the table view.
    let allPhotosOptions = PHFetchOptions()
    allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
    allPhotos = PHAsset.fetchAssets(with: allPhotosOptions)
    let options = PHFetchOptions()
    options.predicate = NSPredicate(format: "estimatedAssetCount > 0")
    options.sortDescriptors = [NSSortDescriptor(key: "localizedTitle", ascending: false)]
    smartAlbums = fetchSmartCollections(with: .smartAlbum, subtypes: subtypes)
    userCollections = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumRegular, options: options)
}

这是我用来获取带有子类型的 PHAssetCollection 的函数:

private func fetchSmartCollections(with: PHAssetCollectionType, subtypes: [PHAssetCollectionSubtype]) -> [PHAssetCollection] {
    var collections:[PHAssetCollection] = []
    let options = PHFetchOptions()
    options.includeHiddenAssets = false

    for subtype in subtypes {
        if let collection = PHAssetCollection.fetchAssetCollections(with: with, subtype: subtype, options: options).firstObject, collection.photosCount > 0 {
            collections.append(collection)
        }
    }

    return collections
}

我在 tableView 中显示结果,然后在 selection 上我将相册中的所有图像传递给另一个控制器。

这是控制器的代码(我们称它为 mMainVC),我实际将图像显示到 select:

fileprivate let imageManager = PHCachingImageManager()
var fetchResult: PHFetchResult<PHAsset>!
var assetCollection: PHAssetCollection!

 override func viewDidLoad() {
    super.viewDidLoad()
if fetchResult == nil {
        let allPhotosOptions = PHFetchOptions()
        allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
        fetchResult = PHAsset.fetchAssets(with: allPhotosOptions)
    }
}

这里是从 fetchResult 获取图像的函数:

func selectImageFromAssetAtIndex(index: Int) {
    let asset = fetchResult.object(at: index)
    let size = scrollViewImage.frame.size.width

    PHImageManager.default().requestImage(for: asset, targetSize: CGSize(width: size, height: size), contentMode: .aspectFill, options: nil, resultHandler: { (image, info) in
        DispatchQueue.main.async {
            self.displayImageInScrollView(image: image!)
        }
    })
}

--------------------屏幕截图和视频

这是视频的 link:https://drive.google.com/file/d/0B6U8olIA_ZS8U0tpN0hKOWdrVm8/view?usp=sharing

我觉得你做的一切都很好,唯一的问题是

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(true)
    let inputImage = CIImage(image: (originalImage.image?.resized(toWidth: 120))!)!
    filteredImages.createImageArray(inputImage: inputImage) {
        self.filteredImagesArray = filteredImages.filteredImages
    }
}

createImageArray 在主线程上调用,这就是您的应用程序冻结的原因。

我会为每个 collectionView cell 添加一个 loadingactivityIndicator 的状态,并且单元格将处于 loading 状态,直到您从createImageArray 调用,然后您将使用过滤后的图像填充单元格,例如:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(true)
    DispatchQueue.global(qos: .userInitiated).async {[weak self] in
        let inputImage = CIImage(image: (originalImage.image?.resized(toWidth: 120))!)!
        filteredImages.createImageArray(inputImage: inputImage) {
            self.filteredImagesArray = filteredImages.filteredImages
            DispatchQueue.main.async {
                let indexPaths = collectionView.indexPathsForVisibleItems

                collectionView.reloadItems(at: indexPaths)
            }
        }
    }
}

因此,在您重新加载可见项目时,将设置 self.filteredImagesArray,您将能够禁用 loading 状态并显示来自 filteredImagesArray[=22 的图像=]