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
添加一个 loading
和 activityIndicator
的状态,并且单元格将处于 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 的图像=]
我正在开发一个应用程序,它可以让用户 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
添加一个 loading
和 activityIndicator
的状态,并且单元格将处于 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 的图像=]