Remove classifier version from database
This commit is contained in:
parent
0e2b0b42ff
commit
344d926b9b
@ -357,6 +357,7 @@ struct ContentView: View {
|
|||||||
private func downloadClassifier() {
|
private func downloadClassifier() {
|
||||||
Task {
|
Task {
|
||||||
await database.downloadClassifier()
|
await database.downloadClassifier()
|
||||||
|
await database.downloadClassifierClasses()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,9 +17,6 @@ struct Cap {
|
|||||||
/// The index of the main image for the cap
|
/// The index of the main image for the cap
|
||||||
var mainImage: Int
|
var mainImage: Int
|
||||||
|
|
||||||
/// The version of the first classifier capable of recognizing the cap
|
|
||||||
var classifierVersion: Int?
|
|
||||||
|
|
||||||
var color: Color?
|
var color: Color?
|
||||||
|
|
||||||
/// The subpath to the main image on the server
|
/// The subpath to the main image on the server
|
||||||
@ -44,13 +41,12 @@ struct Cap {
|
|||||||
- Parameter id: The unique id of the cap
|
- Parameter id: The unique id of the cap
|
||||||
- Parameter name: The name associated with the cap
|
- Parameter name: The name associated with the cap
|
||||||
*/
|
*/
|
||||||
init(id: Int, name: String, classifier: Int? = nil) {
|
init(id: Int, name: String) {
|
||||||
self.id = id
|
self.id = id
|
||||||
self.name = name
|
self.name = name
|
||||||
self.cleanName = name.clean
|
self.cleanName = name.clean
|
||||||
self.imageCount = 0
|
self.imageCount = 0
|
||||||
self.mainImage = 0
|
self.mainImage = 0
|
||||||
self.classifierVersion = classifier
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init(data: CapData) {
|
init(data: CapData) {
|
||||||
@ -59,7 +55,6 @@ struct Cap {
|
|||||||
self.cleanName = data.name.clean
|
self.cleanName = data.name.clean
|
||||||
self.imageCount = data.count
|
self.imageCount = data.count
|
||||||
self.mainImage = data.mainImage
|
self.mainImage = data.mainImage
|
||||||
self.classifierVersion = data.classifierVersion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var data: CapData {
|
var data: CapData {
|
||||||
@ -67,7 +62,6 @@ struct Cap {
|
|||||||
name: name,
|
name: name,
|
||||||
count: imageCount,
|
count: imageCount,
|
||||||
mainImage: mainImage,
|
mainImage: mainImage,
|
||||||
classifierVersion: classifierVersion,
|
|
||||||
color: color)
|
color: color)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,30 +70,18 @@ struct Cap {
|
|||||||
self.cleanName = data.name.clean
|
self.cleanName = data.name.clean
|
||||||
self.imageCount = data.count
|
self.imageCount = data.count
|
||||||
self.mainImage = data.mainImage
|
self.mainImage = data.mainImage
|
||||||
self.classifierVersion = data.classifierVersion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: Cap, rhs: CapData) -> Bool {
|
static func ==(lhs: Cap, rhs: CapData) -> Bool {
|
||||||
lhs.id == rhs.id &&
|
lhs.id == rhs.id &&
|
||||||
lhs.name == rhs.name &&
|
lhs.name == rhs.name &&
|
||||||
lhs.imageCount == rhs.count &&
|
lhs.imageCount == rhs.count &&
|
||||||
lhs.mainImage == rhs.mainImage &&
|
lhs.mainImage == rhs.mainImage
|
||||||
lhs.classifierVersion == rhs.classifierVersion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static func !=(lhs: Cap, rhs: CapData) -> Bool {
|
static func !=(lhs: Cap, rhs: CapData) -> Bool {
|
||||||
!(lhs == rhs)
|
!(lhs == rhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func classifiable(by classifierVersion: Int?) -> Bool {
|
|
||||||
guard let version = classifierVersion else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
guard let own = self.classifierVersion else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return version >= own
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Cap {
|
extension Cap {
|
||||||
@ -130,7 +112,6 @@ extension Cap: Codable {
|
|||||||
case cleanName = "c"
|
case cleanName = "c"
|
||||||
case imageCount = "i"
|
case imageCount = "i"
|
||||||
case mainImage = "m"
|
case mainImage = "m"
|
||||||
case classifierVersion = "v"
|
|
||||||
case color = "f"
|
case color = "f"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,6 @@ struct CapData: Codable {
|
|||||||
|
|
||||||
var mainImage: Int
|
var mainImage: Int
|
||||||
|
|
||||||
var classifierVersion: Int?
|
|
||||||
|
|
||||||
var color: Cap.Color?
|
var color: Cap.Color?
|
||||||
|
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
@ -19,7 +17,6 @@ struct CapData: Codable {
|
|||||||
case name = "n"
|
case name = "n"
|
||||||
case count = "c"
|
case count = "c"
|
||||||
case mainImage = "m"
|
case mainImage = "m"
|
||||||
case classifierVersion = "v"
|
|
||||||
case color = "f"
|
case color = "f"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,6 +106,35 @@ final class Database: ObservableObject {
|
|||||||
|
|
||||||
@Published
|
@Published
|
||||||
var isUploading = false
|
var isUploading = false
|
||||||
|
|
||||||
|
@AppStorage("classifierClasses")
|
||||||
|
private var _classifierClassesString: String = ""
|
||||||
|
|
||||||
|
private var _classifierClassesCache: Set<Int>?
|
||||||
|
|
||||||
|
private var classifierClasses: Set<Int> {
|
||||||
|
get {
|
||||||
|
_classifierClassesCache ?? loadClassifierClasses()
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_classifierClassesCache = newValue
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self._classifierClassesString = newValue.map { "\($0)" }.joined(separator: ",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadClassifierClasses() -> Set<Int> {
|
||||||
|
let elements: [Int] = _classifierClassesString.components(separatedBy: ",").compactMap {
|
||||||
|
guard let id = Int($0) else {
|
||||||
|
log("Failed to load classifier class from '\($0)'")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
_classifierClassesCache = Set(elements)
|
||||||
|
return _classifierClassesCache!
|
||||||
|
}
|
||||||
|
|
||||||
init(server: URL, folder: URL = FileManager.default.documentDirectory) {
|
init(server: URL, folder: URL = FileManager.default.documentDirectory) {
|
||||||
self.serverUrl = server
|
self.serverUrl = server
|
||||||
@ -151,6 +180,10 @@ final class Database: ObservableObject {
|
|||||||
private var serverClassifierUrl: URL {
|
private var serverClassifierUrl: URL {
|
||||||
serverUrl.appendingPathComponent("classifier.mlmodel")
|
serverUrl.appendingPathComponent("classifier.mlmodel")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var serverClassifierClassesUrl: URL {
|
||||||
|
serverUrl.appendingPathComponent("classifier.classes")
|
||||||
|
}
|
||||||
|
|
||||||
private var serverClassifierVersionUrl: URL {
|
private var serverClassifierVersionUrl: URL {
|
||||||
serverUrl.appendingPathComponent("version")
|
serverUrl.appendingPathComponent("version")
|
||||||
@ -359,6 +392,48 @@ final class Database: ObservableObject {
|
|||||||
log("Downloaded classifier \(classifierVersion)")
|
log("Downloaded classifier \(classifierVersion)")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
func downloadClassifierClasses() async -> Bool {
|
||||||
|
log("Downloading classifier classes")
|
||||||
|
let data: Data
|
||||||
|
let response: URLResponse
|
||||||
|
do {
|
||||||
|
(data, response) = try await URLSession.shared.data(from: serverClassifierClassesUrl)
|
||||||
|
} catch {
|
||||||
|
log("Failed to download classifier classes: \(error)")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let string = String(data: data, encoding: .utf8) else {
|
||||||
|
log("Classifier classes is invalid data (not a string)")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let classes = string.components(separatedBy: ",")
|
||||||
|
|
||||||
|
// Validate input
|
||||||
|
var isValid = true
|
||||||
|
let ids: [Int] = classes.compactMap { s in
|
||||||
|
guard let id = Int(s) else {
|
||||||
|
log("Invalid id '\(s)' in downloaded classes list")
|
||||||
|
isValid = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if caps[id] == nil {
|
||||||
|
// Caps which are deleted may still be recognized
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
guard isValid else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
self.classifierClasses = Set(ids)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Indicate that the cap has pending operations, such as determining the color or a thumbnail
|
Indicate that the cap has pending operations, such as determining the color or a thumbnail
|
||||||
@ -374,7 +449,7 @@ final class Database: ObservableObject {
|
|||||||
// MARK: Adding new data
|
// MARK: Adding new data
|
||||||
|
|
||||||
func save(newCap name: String) -> Cap {
|
func save(newCap name: String) -> Cap {
|
||||||
let cap = Cap(id: nextCapId, name: name, classifier: nil)
|
let cap = Cap(id: nextCapId, name: name)
|
||||||
caps[cap.id] = cap
|
caps[cap.id] = cap
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.changedCaps.insert(cap.id)
|
self.changedCaps.insert(cap.id)
|
||||||
@ -745,6 +820,10 @@ final class Database: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func canClassify(cap id: Int) -> Bool {
|
||||||
|
classifierClasses.contains(id)
|
||||||
|
}
|
||||||
|
|
||||||
private func getClassifier() -> Classifier? {
|
private func getClassifier() -> Classifier? {
|
||||||
if let classifier = classifier {
|
if let classifier = classifier {
|
||||||
@ -847,8 +926,7 @@ final class Database: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var classifierClassCount: Int {
|
var classifierClassCount: Int {
|
||||||
let version = classifierVersion
|
classifierClasses.count
|
||||||
return caps.values.filter { $0.classifiable(by: version) }.count
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func imageCacheSize() async -> Int {
|
func imageCacheSize() async -> Int {
|
||||||
@ -883,7 +961,7 @@ extension Database {
|
|||||||
static var largeMock: Database {
|
static var largeMock: Database {
|
||||||
let db = Database(server: URL(string: "https://christophhagen.de/caps")!)
|
let db = Database(server: URL(string: "https://christophhagen.de/caps")!)
|
||||||
db.caps = (1..<500)
|
db.caps = (1..<500)
|
||||||
.map { Cap(id: $0, name: "Cap \($0)", classifier: nil)}
|
.map { Cap(id: $0, name: "Cap \($0)") }
|
||||||
.reduce(into: [:]) { $0[$1.id] = $1 }
|
.reduce(into: [:]) { $0[$1.id] = $1 }
|
||||||
db.image = UIImage(systemSymbol: .photo)
|
db.image = UIImage(systemSymbol: .photo)
|
||||||
return db
|
return db
|
||||||
|
Loading…
Reference in New Issue
Block a user