This RxSwift code is running pretty slow, so can you, please, give me an advise on how to increase it performance?
This function is taking image resources and mapping them into redrawed images
func redrawChapter(pages: [Resource]) -> Single<[UIImage]> {
return Observable
.from(pages)
.flatMap(getImage)
// MARK: Detect synopsys
.flatMap(recognizeText)
// MARK: Translate synopsys
.concatMap(translate)
// MARK: Image redraw
.concatMap(redrawImage)
.toArray()
}
Im using kingfisher to download images
private func getImage(resource: Resource) -> Single<UIImage> {
return Single.create { single in
let disposables = Disposables.create()
KingfisherManager.shared.retrieveImage(with: resource) { result in
switch result {
case .success(let value):
single(.success(value.image))
case .failure(let error):
single(.failure(error))
}
}
return disposables
}
}
Im using apple vision to recognise text regions
private func recognizeText(image: UIImage) -> Single<(UIImage, [Synopsis])> {
return Observable.of(image)
.flatMap {
Observable.combineLatest(
Observable.just($0),
// MARK: Apple vision to detect text regions
self.imageProcessor.getRecognizedText(image: $0).asObservable()
)
}
.asSingle()
}
And Moya for networking
private func translate(image: UIImage, synopsys: [Synopsis]) -> Maybe<(UIImage, [Synopsis])>{
return Observable.from(synopsys)
.flatMap {
Observable.combineLatest(
Observable.just($0.rect),
self.translator.translate(text: $0.text).asObservable()
)
}
.compactMap {
return Synopsis(
text: $0.1,
rect: $0.0
)
}
.toArray()
.compactMap {
return (image, $0)
}
}
Image redraws using UIGraphics renderer
private func redrawImage(
image: UIImage,
synopsys: [Synopsis]
) -> Single<UIImage> {
return Single.create { single in
let format = UIGraphicsImageRendererFormat()
format.scale = 1
guard let cgImage = image.cgImage else {
return Disposables.create()
}
let size = image.size
let bounds = CGRect(
origin: .zero,
size: size
)
let final = UIGraphicsImageRenderer(
bounds: bounds,
format: format
).image { context in
image.draw(in: bounds)
for syn in synopsys {
let backgroundColor = cgImage.averageColorOf(rect: syn.rect)
let textColor = backgroundColor.textColor()
self.setupLabel(
text: syn.text,
backgroundColor: backgroundColor,
textColor: textColor,
bounds: syn.rect,
context: context.cgContext
)
}
}
single(.success(final))
return Disposables.create()
}
}
I feel like there is another way to not to make this huge amount of observables, to make it work, and maybe somehow it will raise this functions performance. Appreciate you for help.
Here's an updated solution. Try it out and see how it goes:
func redrawChapter(pages: [Resource]) -> Single<[UIImage]> {
let image = Observable.from(pages)
.flatMap(getImage(resource:))
let translatedSynopses = image
.flatMap(imageProcessor.getRecognizedText(image:))
.flatMap { Single.zip($0.map(translate(synopses:))) }
return Observable.zip(image, translatedSynopses)
.observe(on: MainScheduler.instance) // if you need it, you may not.
.compactMap(redrawImage(image:synopses:))
.toArray()
}
func getImage(resource: Resource) -> Single<UIImage> {
Single.create { observer in
KingfisherManager.shared.retrieveImage(with: resource, completion: observer)
return Disposables.create()
}
.map(\.image)
}
func translate(synopses: Synopsis) -> Single<Synopsis> {
translator.translate(text: synopses.text)
.map { Synopsis(text: $0, rect: synopses.rect) }
}
func redrawImage(image: UIImage, synopses: [Synopsis]) -> UIImage? {
guard let cgImage = image.cgImage else { return nil }
let format = UIGraphicsImageRendererFormat()
format.scale = 1
let size = image.size
let bounds = CGRect(origin: .zero, size: size)
return UIGraphicsImageRenderer(bounds: bounds, format: format).image { context in
image.draw(in: bounds)
for syn in synopses {
let backgroundColor = cgImage.averageColorOf(rect: syn.rect)
let textColor = backgroundColor.textColor()
setupLabel(
text: syn.text,
backgroundColor: backgroundColor,
textColor: textColor,
bounds: syn.rect,
context: context.cgContext
)
}
}
}
What likely makes this faster? Because I removed the concatMap
s it will perform multiple translations at once (I assume this is a network request.) As the translations for an image come in, it will process that image.
One thing about this code (and yours) is that the image array emitted may be in a different order than the resources passed in. If that's going to be a problem, then adjustments will have to be made.