Skip to content

Commit

Permalink
Merge pull request #174 from cocoatype/173-use-new-matching-for-detec…
Browse files Browse the repository at this point in the history
…tions

Use new matching for detections
  • Loading branch information
Arclite authored Jun 26, 2024
2 parents 93102a0 + 5cec08c commit e00f499
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,16 @@ public struct CharacterObservation: TextObservation, Hashable, RedactableObserva
guard let associatedString, let range else { return nil }
return String(associatedString[range])
}

public func union(with other: CharacterObservation) -> CharacterObservation {
let combiningObservations = [self, other]
guard let associatedString,
let startIndex = combiningObservations.compactMap(\.range?.lowerBound).min(),
let endIndex = combiningObservations.compactMap(\.range?.upperBound).max(),
textObservationUUID == other.textObservationUUID
else { return self }

let range = startIndex..<endIndex
return CharacterObservation(bounds: bounds, textObservationUUID: textObservationUUID, associatedString: associatedString, range: range)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Created by Geoff Pado on 6/25/24.
// Copyright © 2024 Cocoatype, LLC. All rights reserved.

import Foundation

public struct CombinedCharacterObservation {
public let textObservationUUID: UUID
private let characterObservations: [CharacterObservation]
public let associatedString: String

public init?(characterObservations: [CharacterObservation]) {
guard let firstUUID = characterObservations.first?.textObservationUUID,
characterObservations.allSatisfy({ $0.textObservationUUID == firstUUID }),
let firstString = characterObservations.first?.associatedString
else { return nil }

self.textObservationUUID = firstUUID
self.characterObservations = characterObservations
self.associatedString = firstString
}

public func characterObservations(with substrings: [Substring]) -> [CharacterObservation] {
characterObservations.filter { observation in
guard let observationRange = observation.range else { return false }
return substrings.contains { substring in
let substringRange = substring.startIndex ..< substring.endIndex
return observationRange.overlaps(substringRange)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,7 @@ public struct RecognizedTextObservation: TextObservation, RedactableObservation
return CharacterObservation(bounds: shape, textObservationUUID: recognizedText.uuid, associatedString: visionText.string, range: characterRange)
}.reduce(into: [Shape: CharacterObservation]()) { uniqueObservations, observation in
if let existingObservation = uniqueObservations[observation.bounds] {
let combiningObservations = [existingObservation, observation]
guard let associatedString = existingObservation.associatedString,
let startIndex = combiningObservations.compactMap(\.range?.lowerBound).min(),
let endIndex = combiningObservations.compactMap(\.range?.upperBound).max()
else { return }
let range = startIndex..<endIndex
let combinedObservation = CharacterObservation(bounds: observation.bounds, textObservationUUID: observation.textObservationUUID, associatedString: associatedString, range: range)
uniqueObservations[observation.bounds] = combinedObservation
uniqueObservations[observation.bounds] = existingObservation.union(with: observation)
} else {
uniqueObservations[observation.bounds] = observation
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,19 +82,6 @@ actor PhotoEditingObservationCalculator {
}

var calculatedObservationsByUUID: [UUID: [CharacterObservation]] {
return calculatedObservations.reduce([UUID: [CharacterObservation]]()) { dictionary, observation in
var observationsByUUID: [CharacterObservation]
if let existing = dictionary[observation.textObservationUUID] {
observationsByUUID = existing
} else {
observationsByUUID = []
}

observationsByUUID.append(observation)

var newDictionary = dictionary
newDictionary[observation.textObservationUUID] = observationsByUUID
return newDictionary
}
return calculatedObservations.byUUID
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Created by Geoff Pado on 6/25/24.
// Copyright © 2024 Cocoatype, LLC. All rights reserved.

import Foundation
import Observations

extension [CharacterObservation] {
Expand All @@ -13,4 +14,21 @@ extension [CharacterObservation] {
return trimmedAssociatedWord.compare(trimmedText, options: [.caseInsensitive, .diacriticInsensitive]) == .orderedSame
}
}

var byUUID: [UUID: [CharacterObservation]] {
reduce([UUID: [CharacterObservation]]()) { dictionary, observation in
var observationsByUUID: [CharacterObservation]
if let existing = dictionary[observation.textObservationUUID] {
observationsByUUID = existing
} else {
observationsByUUID = []
}

observationsByUUID.append(observation)

var newDictionary = dictionary
newDictionary[observation.textObservationUUID] = observationsByUUID
return newDictionary
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Defaults
import Detections
import ErrorHandling
import Exporting
import Geometry
import Observations
import Photos
import PurchaseMarketing
Expand Down Expand Up @@ -404,12 +405,20 @@ public class PhotoEditingViewController: UIViewController, UIScrollViewDelegate,
taggingFunctions.append( Category.phoneNumbers.getFuncyInSwizzleTown)
}

let textObservations = photoEditingView.recognizedTextObservations ?? []
let categoryObservations = textObservations.flatMap { text in
taggingFunctions
.flatMap { function in function(text.string) }
.compactMap { text.wordObservation(for: $0) }
}
let combinedObservations = photoEditingView.redactableCharacterObservations.byUUID

let categoryObservations = combinedObservations
.values
.compactMap(CombinedCharacterObservation.init)
.flatMap { combinedObservation in
taggingFunctions
.map { function in function(combinedObservation.associatedString) }
.map {
let matchingObservations = combinedObservation.characterObservations(with: $0)
let combinedShape = MinimumAreaShapeFinder.minimumAreaShape(for: matchingObservations.map(\.bounds))
return CharacterObservation(bounds: combinedShape, textObservationUUID: combinedObservation.textObservationUUID)
}
}

return wordObservations + categoryObservations
}
Expand Down

0 comments on commit e00f499

Please sign in to comment.