Verified Commit 6ff8e9da authored by Geoff Pado's avatar Geoff Pado

Generate paths where text is redacted.

parent 634ce038
This diff is collapsed.
......@@ -25,12 +25,11 @@ class PhotoEditingObservationVisualizationView: UIView {
boundsPath.fill()
boundsPath.stroke()
observation.characterObservations?.forEach { characterObservation in
let isRedacted = redactedCharacterObservations?.contains(characterObservation) ?? false
let baseColor = isRedacted ? UIColor.green : UIColor.blue
baseColor.withAlphaComponent(0.3).setFill()
baseColor.setStroke()
let baseColor = UIColor.blue
baseColor.withAlphaComponent(0.3).setFill()
baseColor.setStroke()
observation.characterObservations?.forEach { characterObservation in
let boundsPath = UIBezierPath(rect: characterObservation.bounds)
boundsPath.fill()
boundsPath.stroke()
......@@ -38,14 +37,7 @@ class PhotoEditingObservationVisualizationView: UIView {
}
}
var textObservations: [DetectedTextObservation]? {
didSet {
setNeedsDisplay()
}
}
#warning("Do not merge; only for temporary debugging purposes")
var redactedCharacterObservations: [DetectedCharacterObservation]? {
var textObservations: [TextObservation]? {
didSet {
setNeedsDisplay()
}
......
// Created by Geoff Pado on 5/6/19.
// Copyright © 2019 Cocoatype, LLC. All rights reserved.
import UIKit
class PhotoEditingRedactionView: UIView {
init() {
super.init(frame: .zero)
backgroundColor = .clear
isOpaque = false
translatesAutoresizingMaskIntoConstraints = false
}
override func draw(_ rect: CGRect) {
super.draw(rect)
UIColor.green.setStroke()
redactions.forEach { redaction in
let path = redaction.path
path.lineWidth = 10.0
path.stroke()
}
}
func add(_ redaction: Redaction) {
redactions.append(redaction)
setNeedsDisplay(redaction.path.bounds)
}
// MARK: Boilerplate
private var redactions = [Redaction]()
@available(*, unavailable)
required init(coder: NSCoder) {
let className = String(describing: type(of: self))
fatalError("\(className) does not implement init(coder:)")
}
}
......@@ -32,7 +32,7 @@ class PhotoEditingScrollView: UIScrollView {
}
}
var textObservations: [DetectedTextObservation]? {
var textObservations: [TextObservation]? {
get { return photoEditingView.textObservations }
set(newTextObservations) {
photoEditingView.textObservations = newTextObservations
......
......@@ -7,6 +7,7 @@ class PhotoEditingView: UIView {
init() {
imageView = PhotoEditingImageView()
visualizationView = PhotoEditingObservationVisualizationView()
redactionView = PhotoEditingRedactionView()
brushStrokeView = PhotoEditingBrushStrokeView()
super.init(frame: .zero)
......@@ -15,6 +16,7 @@ class PhotoEditingView: UIView {
addSubview(imageView)
addSubview(visualizationView)
addSubview(redactionView)
addSubview(brushStrokeView)
NSLayoutConstraint.activate([
......@@ -26,6 +28,10 @@ class PhotoEditingView: UIView {
visualizationView.centerYAnchor.constraint(equalTo: centerYAnchor),
visualizationView.widthAnchor.constraint(equalTo: widthAnchor),
visualizationView.heightAnchor.constraint(equalTo: heightAnchor),
redactionView.centerXAnchor.constraint(equalTo: centerXAnchor),
redactionView.centerYAnchor.constraint(equalTo: centerYAnchor),
redactionView.widthAnchor.constraint(equalTo: widthAnchor),
redactionView.heightAnchor.constraint(equalTo: heightAnchor),
brushStrokeView.centerXAnchor.constraint(equalTo: centerXAnchor),
brushStrokeView.centerYAnchor.constraint(equalTo: centerYAnchor),
brushStrokeView.widthAnchor.constraint(equalTo: widthAnchor),
......@@ -42,7 +48,7 @@ class PhotoEditingView: UIView {
}
}
var textObservations: [DetectedTextObservation]? {
var textObservations: [TextObservation]? {
get { return visualizationView.textObservations }
set(newTextObservations) {
visualizationView.textObservations = newTextObservations
......@@ -54,16 +60,19 @@ class PhotoEditingView: UIView {
@objc func handleStrokeCompletion() {
guard let strokePath = brushStrokeView.currentPath, let textObservations = textObservations else { return }
let strokeBorderPath = strokePath.strokeBorderPath
visualizationView.redactedCharacterObservations = textObservations
let redactedCharacterObservations = textObservations
.compactMap { $0.characterObservations }
.flatMap { $0 }
.filter { strokeBorderPath.contains($0.bounds.center) }
redactionView.add(CharacterObservationRedaction(redactedCharacterObservations))
}
// MARK: Boilerplate
private let imageView: PhotoEditingImageView
private let visualizationView: PhotoEditingObservationVisualizationView
private let redactionView: PhotoEditingRedactionView
private let brushStrokeView: PhotoEditingBrushStrokeView
@available(*, unavailable)
......
// Created by Geoff Pado on 5/6/19.
// Copyright © 2019 Cocoatype, LLC. All rights reserved.
import UIKit
protocol Redaction {
var path: UIBezierPath { get }
}
struct CharacterObservationRedaction: Redaction {
init(_ characterObservations: [CharacterObservation]) {
self.characterObservations = characterObservations
self.path = characterObservations.reduce(into: [UUID: [CharacterObservation]]()) { result, characterObservation in
let textObservationUUID = characterObservation.textObservationUUID
var siblingObservations = result[textObservationUUID] ?? []
siblingObservations.append(characterObservation)
result[textObservationUUID] = siblingObservations
}.values.map { siblingObservations in
siblingObservations.reduce(siblingObservations[0].bounds, { currentRect, characterObservation in
currentRect.union(characterObservation.bounds)
})
}.reduce(into: UIBezierPath(), { path, rect in
path.move(to: CGPoint(x: rect.minX, y: rect.midY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.midY))
})
}
let path: UIBezierPath
private let characterObservations: [CharacterObservation]
}
......@@ -3,6 +3,7 @@
import UIKit
struct DetectedCharacterObservation: Equatable {
struct CharacterObservation: Equatable {
let bounds: CGRect
let textObservationUUID: UUID
}
......@@ -4,17 +4,21 @@
import UIKit
import Vision
struct DetectedTextObservation: Equatable {
struct TextObservation: Equatable {
init(_ textObservation: VNTextObservation, in image: UIImage) {
let boundingBox = textObservation.boundingBox
let imageSize = image.size * image.scale
self.bounds = CGRect.flippedRect(from: boundingBox, scaledTo: imageSize)
let observationUUID = textObservation.uuid
self.uuid = observationUUID
self.characterObservations = textObservation.characterBoxes?.map {
DetectedCharacterObservation(bounds: CGRect.flippedRect(from: $0.boundingBox, scaledTo: imageSize))
CharacterObservation(bounds: CGRect.flippedRect(from: $0.boundingBox, scaledTo: imageSize), textObservationUUID: observationUUID)
}
}
let bounds: CGRect
let characterObservations: [DetectedCharacterObservation]?
let characterObservations: [CharacterObservation]?
let uuid: UUID
}
......@@ -4,14 +4,14 @@
import UIKit
class TextRectangleDetector: NSObject {
func detectTextRectangles(in image: UIImage, completionHandler: (([DetectedTextObservation]?) -> Void)? = nil) {
func detectTextRectangles(in image: UIImage, completionHandler: (([TextObservation]?) -> Void)? = nil) {
guard let detectionOperation = TextRectangleDetectionOperation(image: image) else {
completionHandler?(nil)
return
}
detectionOperation.completionBlock = { [weak detectionOperation] in
let detectedTextObservations = detectionOperation?.textRectangleResults?.map { DetectedTextObservation($0, in: image) }
let detectedTextObservations = detectionOperation?.textRectangleResults?.map { TextObservation($0, in: image) }
completionHandler?(detectedTextObservations)
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment