Caps-iOS/CapCollector/Presentation/SettingsController.swift
Christoph Hagen dceb3ca07d - Remove Xcode and Regnet classifier
- Add MobileNet classifier
- Add average color for each cap
- Add option to show average colors in mosaic
2019-07-17 11:10:07 +02:00

273 lines
8.1 KiB
Swift

//
// SettingsController.swift
// CapCollector
//
// Created by Christoph on 15.10.18.
// Copyright © 2018 CH. All rights reserved.
//
import UIKit
class SettingsController: UITableViewController {
@IBOutlet weak var totalCapsLabel: UILabel!
@IBOutlet weak var totalImagesLabel: UILabel!
@IBOutlet weak var recognizedCapsLabel: UILabel!
@IBOutlet weak var imagesStatsLabel: UILabel!
@IBOutlet weak var databaseUpdatesLabel: UILabel!
@IBOutlet weak var dropboxAccountLabel: UILabel!
@IBOutlet weak var countsLabel: UILabel!
private var isUpdatingCounts = false
private var isUploadingNameFile = false
private var isUpdatingThumbnails = false
private var isUpdatingColors = false
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
updateDropboxStatus()
updateNameFileStats()
updateDatabaseStats()
(navigationController as! NavigationController).allowLandscape = false
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
setClassifierChoice(Persistence.useMobileNet)
}
private func updateThumbnails() {
isUpdatingThumbnails = true
for cap in Cap.all.values {
cap.makeThumbnail()
}
isUpdatingThumbnails = false
}
private func updateColors() {
isUpdatingColors = true
Cap.shouldSave = false
for cap in Cap.all.values {
cap.makeAverageColor()
}
Cap.shouldSave = true
isUpdatingColors = false
}
private func updateDatabaseStats() {
let totalCaps = Cap.totalCapCount
totalCapsLabel.text = "\(totalCaps) caps"
totalImagesLabel.text = "\(Cap.imageCount) images"
let recognizedCaps = Persistence.recognizedCapCount
let newCapCount = totalCaps - recognizedCaps
recognizedCapsLabel.text = "\(newCapCount) new caps"
let ratio = Float(Cap.imageCount)/Float(Cap.totalCapCount)
let (images, count) = Cap.getCapStatistics().enumerated().first(where: { $0.element != 0 })!
imagesStatsLabel.text = String(format: "%.2f images per cap, lowest count: %d (%dx)", ratio, images, count)
}
private func updateNameFileStats() {
let capCount = Cap.totalCapCount - Persistence.lastUploadedCapCount
let imageCount = Cap.imageCount - Persistence.lastUploadedImageCount
databaseUpdatesLabel.text = "\(capCount) new caps and \(imageCount) new images"
}
private func updateDropboxStatus() {
dropboxAccountLabel.text = DropboxController.shared.isEnabled ? "Sign out" : "Sign in"
}
private func setClassifierChoice(_ useMobileNet: Bool) {
tableView.cellForRow(at: IndexPath(row: 0, section: 0))?.accessoryType = useMobileNet ? .checkmark : .none
}
private func toggleClassifier() {
let newValue = !Persistence.useMobileNet
Persistence.useMobileNet = newValue
setClassifierChoice(newValue)
}
override func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
switch indexPath.section {
case 0: // Choose models
return true
case 1: // Mosaic
return true
case 2: // Database
return indexPath.row == 2 && !isUploadingNameFile
case 3: // Refresh
switch indexPath.row {
case 0:
return !isUpdatingCounts
case 1:
return !isUpdatingThumbnails
case 2:
return !isUpdatingColors
default:
return false
}
case 4: // Dropbox account
return true
case 5: // Log file
return true
default: return false
}
}
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
switch indexPath.section {
case 0: // Choose models
return indexPath
case 1: // Mosaic
return indexPath
case 2: // Database
return (indexPath.row == 2 && !isUploadingNameFile) ? indexPath : nil
case 3: // Refresh count
switch indexPath.row {
case 0:
return isUpdatingCounts ? nil : indexPath
case 1:
return isUpdatingThumbnails ? nil : indexPath
case 2:
return isUpdatingColors ? nil : indexPath
default:
return nil
}
case 4: // Dropbox account
return indexPath
case 5: // Log file
return indexPath
default: return nil
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
switch indexPath.section {
case 0: // Choose models
toggleClassifier()
case 2: // Upload
if indexPath.row == 2 && !isUploadingNameFile {
uploadNameFile()
}
case 3: // Refresh count
switch indexPath.row {
case 0:
updateCounts()
case 1:
updateThumbnails()
case 2:
updateColors()
default:
break
}
default:
break
}
}
private func updateCounts() {
isUpdatingCounts = true
Cap.shouldSave = false
// TODO: Don't make all requests at the same time
DispatchQueue.global(qos: .userInitiated).async {
let list = Cap.all.keys.sorted()
let total = list.count
var finished = 0
let chunks = list.chunked(into: 10)
for chunk in chunks {
self.updateCounts(ids: chunk)
finished += 10
DispatchQueue.main.async {
self.countsLabel.text = "\(finished) / \(total) finished"
}
}
self.isUpdatingCounts = false
Cap.shouldSave = true
DispatchQueue.main.async {
self.countsLabel.text = "Refresh image counts"
self.updateDatabaseStats()
}
}
}
private func updateCounts(ids: [Int]) {
var count = ids.count
let s = DispatchSemaphore(value: 0)
for cap in ids {
Cap.all[cap]!.updateCount { _ in
count -= 1
if count == 0 {
s.signal()
}
}
}
_ = s.wait(timeout: .now() + .seconds(30))
event("Finished updating ids \(ids.first!) to \(ids.last!)")
}
private func uploadNameFile() {
event("Uploading name file")
isUploadingNameFile = true
Cap.saveAndUpload() { _ in
self.isUploadingNameFile = false
self.updateNameFileStats()
}
}
private func toggleDropbox() {
guard !DropboxController.shared.isEnabled else {
DropboxController.shared.signOut()
updateDropboxStatus()
return
}
DropboxController.shared.setup(in: self)
updateDropboxStatus()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let id = segue.identifier else {
return
}
switch id {
case "showMosaic":
(navigationController as! NavigationController).allowLandscape = true
case "showLog":
return
default:
return
}
}
}
extension SettingsController: Logger {
static let logToken = "[Settings]"
}
private extension Array {
func chunked(into size: Int) -> [[Element]] {
return stride(from: 0, to: count, by: size).map {
Array(self[$0 ..< Swift.min($0 + size, count)])
}
}
}