From 12340b87e56df49a08854b274c1e863021d7bbb3 Mon Sep 17 00:00:00 2001 From: Maciej Czajka Date: Fri, 27 Jan 2023 00:11:51 +0100 Subject: [PATCH] add app for iOS --- .../LicensePlates.xcodeproj/project.pbxproj | 457 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/swiftpm/Package.resolved | 14 + .../UserInterfaceState.xcuserstate | Bin 0 -> 69573 bytes .../xcdebugger/Breakpoints_v2.xcbkptlist | 168 +++++++ .../xcschemes/xcschememanagement.plist | 14 + .../LicensePlates/Another/ImagePicker.swift | 187 +++++++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 13 + .../Assets.xcassets/Contents.json | 6 + .../LicensePlates/Controller/Api.swift | 96 ++++ .../LicensePlates/Data/PlatesList.swift | 17 + .../LicensePlates/Data/ResponseData.swift | 17 + .../LicensePlates/Data/WaitData.swift | 14 + .../LicensePlates/LicensePlatesApp.swift | 17 + .../Model/LicensePlateItem.swift | 14 + .../LicensePlates/Model/ResponseItem.swift | 12 + .../Preview Assets.xcassets/Contents.json | 6 + .../LicensePlates/View/ContentView.swift | 199 ++++++++ .../LicensePlates/View/LoaderView.swift | 25 + .../LicensePlates/View/MenuView.swift | 32 ++ .../LicensePlates/View/PlatesListView.swift | 81 ++++ .../LicensePlates/View/ResultView.swift | 31 ++ api.py | 58 ++- yolo_video.py | 46 +- 26 files changed, 1483 insertions(+), 67 deletions(-) create mode 100644 App/LicensePlates/LicensePlates.xcodeproj/project.pbxproj create mode 100644 App/LicensePlates/LicensePlates.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 App/LicensePlates/LicensePlates.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 App/LicensePlates/LicensePlates.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 App/LicensePlates/LicensePlates.xcodeproj/project.xcworkspace/xcuserdata/maciej.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 App/LicensePlates/LicensePlates.xcodeproj/xcuserdata/maciej.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist create mode 100644 App/LicensePlates/LicensePlates.xcodeproj/xcuserdata/maciej.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 App/LicensePlates/LicensePlates/Another/ImagePicker.swift create mode 100644 App/LicensePlates/LicensePlates/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 App/LicensePlates/LicensePlates/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 App/LicensePlates/LicensePlates/Assets.xcassets/Contents.json create mode 100644 App/LicensePlates/LicensePlates/Controller/Api.swift create mode 100644 App/LicensePlates/LicensePlates/Data/PlatesList.swift create mode 100644 App/LicensePlates/LicensePlates/Data/ResponseData.swift create mode 100644 App/LicensePlates/LicensePlates/Data/WaitData.swift create mode 100644 App/LicensePlates/LicensePlates/LicensePlatesApp.swift create mode 100644 App/LicensePlates/LicensePlates/Model/LicensePlateItem.swift create mode 100644 App/LicensePlates/LicensePlates/Model/ResponseItem.swift create mode 100644 App/LicensePlates/LicensePlates/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 App/LicensePlates/LicensePlates/View/ContentView.swift create mode 100644 App/LicensePlates/LicensePlates/View/LoaderView.swift create mode 100644 App/LicensePlates/LicensePlates/View/MenuView.swift create mode 100644 App/LicensePlates/LicensePlates/View/PlatesListView.swift create mode 100644 App/LicensePlates/LicensePlates/View/ResultView.swift diff --git a/App/LicensePlates/LicensePlates.xcodeproj/project.pbxproj b/App/LicensePlates/LicensePlates.xcodeproj/project.pbxproj new file mode 100644 index 0000000..dcd4c6c --- /dev/null +++ b/App/LicensePlates/LicensePlates.xcodeproj/project.pbxproj @@ -0,0 +1,457 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 52A4E0F72982BC4300C09B44 /* ImagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52A4E0F62982BC4300C09B44 /* ImagePicker.swift */; }; + 52A4E0FD2982F76800C09B44 /* ResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52A4E0FC2982F76800C09B44 /* ResultView.swift */; }; + 52A4E0FF29830C2D00C09B44 /* LoaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52A4E0FE29830C2D00C09B44 /* LoaderView.swift */; }; + 52A4E10129830C6200C09B44 /* WaitData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52A4E10029830C6200C09B44 /* WaitData.swift */; }; + 52A4E103298312BC00C09B44 /* MenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52A4E102298312BC00C09B44 /* MenuView.swift */; }; + 52A4E1052983133200C09B44 /* PlatesListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52A4E1042983133200C09B44 /* PlatesListView.swift */; }; + 52A4E10E2983166400C09B44 /* PlatesList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52A4E10D2983166400C09B44 /* PlatesList.swift */; }; + 52A4E11029831B5000C09B44 /* LicensePlateItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52A4E10F29831B5000C09B44 /* LicensePlateItem.swift */; }; + 52F7EB5129814DC600125573 /* LicensePlatesApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52F7EB5029814DC600125573 /* LicensePlatesApp.swift */; }; + 52F7EB5329814DC600125573 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52F7EB5229814DC600125573 /* ContentView.swift */; }; + 52F7EB5529814DC700125573 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 52F7EB5429814DC700125573 /* Assets.xcassets */; }; + 52F7EB5829814DC700125573 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 52F7EB5729814DC700125573 /* Preview Assets.xcassets */; }; + 52F7EB5F2981510800125573 /* ResponseItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52F7EB5E2981510800125573 /* ResponseItem.swift */; }; + 52F7EB612981516000125573 /* Api.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52F7EB602981516000125573 /* Api.swift */; }; + 52F7EB642981519300125573 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 52F7EB632981519300125573 /* Alamofire */; }; + 52F7EB66298151B800125573 /* ResponseData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52F7EB65298151B800125573 /* ResponseData.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 52A4E0F62982BC4300C09B44 /* ImagePicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImagePicker.swift; sourceTree = ""; }; + 52A4E0FC2982F76800C09B44 /* ResultView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResultView.swift; sourceTree = ""; }; + 52A4E0FE29830C2D00C09B44 /* LoaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoaderView.swift; sourceTree = ""; }; + 52A4E10029830C6200C09B44 /* WaitData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitData.swift; sourceTree = ""; }; + 52A4E102298312BC00C09B44 /* MenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuView.swift; sourceTree = ""; }; + 52A4E1042983133200C09B44 /* PlatesListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatesListView.swift; sourceTree = ""; }; + 52A4E10D2983166400C09B44 /* PlatesList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatesList.swift; sourceTree = ""; }; + 52A4E10F29831B5000C09B44 /* LicensePlateItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicensePlateItem.swift; sourceTree = ""; }; + 52F7EB4D29814DC600125573 /* LicensePlates.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LicensePlates.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 52F7EB5029814DC600125573 /* LicensePlatesApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicensePlatesApp.swift; sourceTree = ""; }; + 52F7EB5229814DC600125573 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 52F7EB5429814DC700125573 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 52F7EB5729814DC700125573 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 52F7EB5E2981510800125573 /* ResponseItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseItem.swift; sourceTree = ""; }; + 52F7EB602981516000125573 /* Api.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Api.swift; sourceTree = ""; }; + 52F7EB65298151B800125573 /* ResponseData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseData.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 52F7EB4A29814DC600125573 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 52F7EB642981519300125573 /* Alamofire in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 52A4E1062983156600C09B44 /* View */ = { + isa = PBXGroup; + children = ( + 52A4E102298312BC00C09B44 /* MenuView.swift */, + 52F7EB5229814DC600125573 /* ContentView.swift */, + 52A4E0FC2982F76800C09B44 /* ResultView.swift */, + 52A4E1042983133200C09B44 /* PlatesListView.swift */, + 52A4E0FE29830C2D00C09B44 /* LoaderView.swift */, + ); + path = View; + sourceTree = ""; + }; + 52A4E1072983158900C09B44 /* Model */ = { + isa = PBXGroup; + children = ( + 52F7EB5E2981510800125573 /* ResponseItem.swift */, + 52A4E10F29831B5000C09B44 /* LicensePlateItem.swift */, + ); + path = Model; + sourceTree = ""; + }; + 52A4E1082983159400C09B44 /* Data */ = { + isa = PBXGroup; + children = ( + 52A4E10D2983166400C09B44 /* PlatesList.swift */, + 52F7EB65298151B800125573 /* ResponseData.swift */, + 52A4E10029830C6200C09B44 /* WaitData.swift */, + ); + path = Data; + sourceTree = ""; + }; + 52A4E109298315A400C09B44 /* Another */ = { + isa = PBXGroup; + children = ( + 52A4E0F62982BC4300C09B44 /* ImagePicker.swift */, + ); + path = Another; + sourceTree = ""; + }; + 52A4E10C2983164C00C09B44 /* Controller */ = { + isa = PBXGroup; + children = ( + 52F7EB602981516000125573 /* Api.swift */, + ); + path = Controller; + sourceTree = ""; + }; + 52F7EB4429814DC600125573 = { + isa = PBXGroup; + children = ( + 52F7EB4F29814DC600125573 /* LicensePlates */, + 52F7EB4E29814DC600125573 /* Products */, + ); + sourceTree = ""; + }; + 52F7EB4E29814DC600125573 /* Products */ = { + isa = PBXGroup; + children = ( + 52F7EB4D29814DC600125573 /* LicensePlates.app */, + ); + name = Products; + sourceTree = ""; + }; + 52F7EB4F29814DC600125573 /* LicensePlates */ = { + isa = PBXGroup; + children = ( + 52F7EB5029814DC600125573 /* LicensePlatesApp.swift */, + 52A4E1062983156600C09B44 /* View */, + 52A4E10C2983164C00C09B44 /* Controller */, + 52A4E1082983159400C09B44 /* Data */, + 52A4E1072983158900C09B44 /* Model */, + 52A4E109298315A400C09B44 /* Another */, + 52F7EB5429814DC700125573 /* Assets.xcassets */, + 52F7EB5629814DC700125573 /* Preview Content */, + ); + path = LicensePlates; + sourceTree = ""; + }; + 52F7EB5629814DC700125573 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 52F7EB5729814DC700125573 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 52F7EB4C29814DC600125573 /* LicensePlates */ = { + isa = PBXNativeTarget; + buildConfigurationList = 52F7EB5B29814DC700125573 /* Build configuration list for PBXNativeTarget "LicensePlates" */; + buildPhases = ( + 52F7EB4929814DC600125573 /* Sources */, + 52F7EB4A29814DC600125573 /* Frameworks */, + 52F7EB4B29814DC600125573 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = LicensePlates; + packageProductDependencies = ( + 52F7EB632981519300125573 /* Alamofire */, + ); + productName = LicensePlates; + productReference = 52F7EB4D29814DC600125573 /* LicensePlates.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 52F7EB4529814DC600125573 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1420; + LastUpgradeCheck = 1420; + TargetAttributes = { + 52F7EB4C29814DC600125573 = { + CreatedOnToolsVersion = 14.2; + }; + }; + }; + buildConfigurationList = 52F7EB4829814DC600125573 /* Build configuration list for PBXProject "LicensePlates" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 52F7EB4429814DC600125573; + packageReferences = ( + 52F7EB622981519300125573 /* XCRemoteSwiftPackageReference "Alamofire" */, + ); + productRefGroup = 52F7EB4E29814DC600125573 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 52F7EB4C29814DC600125573 /* LicensePlates */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 52F7EB4B29814DC600125573 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 52F7EB5829814DC700125573 /* Preview Assets.xcassets in Resources */, + 52F7EB5529814DC700125573 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 52F7EB4929814DC600125573 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 52A4E10E2983166400C09B44 /* PlatesList.swift in Sources */, + 52F7EB5329814DC600125573 /* ContentView.swift in Sources */, + 52F7EB5F2981510800125573 /* ResponseItem.swift in Sources */, + 52F7EB66298151B800125573 /* ResponseData.swift in Sources */, + 52A4E0F72982BC4300C09B44 /* ImagePicker.swift in Sources */, + 52F7EB612981516000125573 /* Api.swift in Sources */, + 52A4E103298312BC00C09B44 /* MenuView.swift in Sources */, + 52A4E0FF29830C2D00C09B44 /* LoaderView.swift in Sources */, + 52A4E10129830C6200C09B44 /* WaitData.swift in Sources */, + 52A4E1052983133200C09B44 /* PlatesListView.swift in Sources */, + 52A4E0FD2982F76800C09B44 /* ResultView.swift in Sources */, + 52F7EB5129814DC600125573 /* LicensePlatesApp.swift in Sources */, + 52A4E11029831B5000C09B44 /* LicensePlateItem.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 52F7EB5929814DC700125573 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.2; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 52F7EB5A29814DC700125573 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.2; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 52F7EB5C29814DC700125573 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"LicensePlates/Preview Content\""; + DEVELOPMENT_TEAM = VKJ3MY4S58; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.maciejczajka.LicensePlates; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 52F7EB5D29814DC700125573 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = ""; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"LicensePlates/Preview Content\""; + DEVELOPMENT_TEAM = VKJ3MY4S58; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.maciejczajka.LicensePlates; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 52F7EB4829814DC600125573 /* Build configuration list for PBXProject "LicensePlates" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 52F7EB5929814DC700125573 /* Debug */, + 52F7EB5A29814DC700125573 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 52F7EB5B29814DC700125573 /* Build configuration list for PBXNativeTarget "LicensePlates" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 52F7EB5C29814DC700125573 /* Debug */, + 52F7EB5D29814DC700125573 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 52F7EB622981519300125573 /* XCRemoteSwiftPackageReference "Alamofire" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Alamofire/Alamofire.git"; + requirement = { + branch = master; + kind = branch; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 52F7EB632981519300125573 /* Alamofire */ = { + isa = XCSwiftPackageProductDependency; + package = 52F7EB622981519300125573 /* XCRemoteSwiftPackageReference "Alamofire" */; + productName = Alamofire; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 52F7EB4529814DC600125573 /* Project object */; +} diff --git a/App/LicensePlates/LicensePlates.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/App/LicensePlates/LicensePlates.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/App/LicensePlates/LicensePlates.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/App/LicensePlates/LicensePlates.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/App/LicensePlates/LicensePlates.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/App/LicensePlates/LicensePlates.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/App/LicensePlates/LicensePlates.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/App/LicensePlates/LicensePlates.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..8881f7f --- /dev/null +++ b/App/LicensePlates/LicensePlates.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "alamofire", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Alamofire/Alamofire.git", + "state" : { + "branch" : "master", + "revision" : "f1f7a06f40c6aadb4beb2a78f380f9938d574fa7" + } + } + ], + "version" : 2 +} diff --git a/App/LicensePlates/LicensePlates.xcodeproj/project.xcworkspace/xcuserdata/maciej.xcuserdatad/UserInterfaceState.xcuserstate b/App/LicensePlates/LicensePlates.xcodeproj/project.xcworkspace/xcuserdata/maciej.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..78b5e0807fef87d9b9d8e6e1fac7532944dc7739 GIT binary patch literal 69573 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 literal 0 HcmV?d00001 diff --git a/App/LicensePlates/LicensePlates.xcodeproj/xcuserdata/maciej.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/App/LicensePlates/LicensePlates.xcodeproj/xcuserdata/maciej.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..6d6c711 --- /dev/null +++ b/App/LicensePlates/LicensePlates.xcodeproj/xcuserdata/maciej.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/App/LicensePlates/LicensePlates.xcodeproj/xcuserdata/maciej.xcuserdatad/xcschemes/xcschememanagement.plist b/App/LicensePlates/LicensePlates.xcodeproj/xcuserdata/maciej.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..581ab6b --- /dev/null +++ b/App/LicensePlates/LicensePlates.xcodeproj/xcuserdata/maciej.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + LicensePlates.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/App/LicensePlates/LicensePlates/Another/ImagePicker.swift b/App/LicensePlates/LicensePlates/Another/ImagePicker.swift new file mode 100644 index 0000000..de7f660 --- /dev/null +++ b/App/LicensePlates/LicensePlates/Another/ImagePicker.swift @@ -0,0 +1,187 @@ +// +// ImagePicker.swift +// LicensePlates +// +// Created by Maciej Czajka on 26/01/2023. +// + +import SwiftUI +import PhotosUI +import UIKit + +struct ImagePicker: UIViewControllerRepresentable { + + class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate { + let parent: ImagePicker + + init(_ parent: ImagePicker) { + self.parent = parent + } + + func imagePickerController(_ picker: UIImagePickerController, + didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) { + if let uiImage = info[.originalImage] as? UIImage { + parent.image = uiImage + } + parent.presentationMode.wrappedValue.dismiss() + } + } + + @Environment(\.presentationMode) var presentationMode + @Binding var image: UIImage? + + func makeCoordinator() -> Coordinator { + Coordinator(self) + } + + func makeUIViewController(context: UIViewControllerRepresentableContext) -> UIImagePickerController { + let picker = UIImagePickerController() + picker.delegate = context.coordinator + return picker + } + + func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext) { + // nop + } +} + +struct CameraPicker: UIViewControllerRepresentable { + + var sourceType: UIImagePickerController.SourceType = .photoLibrary + + @Binding var selectedImage: UIImage? + @Environment(\.presentationMode) var presentationMode + + func makeUIViewController(context: UIViewControllerRepresentableContext) -> UIImagePickerController { + + let imagePicker = UIImagePickerController() + imagePicker.allowsEditing = false + imagePicker.sourceType = sourceType + imagePicker.delegate = context.coordinator + + return imagePicker + } + + func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext) { + // nop + } + + func makeCoordinator() -> Coordinator { + Coordinator(self) + } + + final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate { + + var parent: CameraPicker + + init(_ parent: CameraPicker) { + self.parent = parent + } + + func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { + + if let image = info[.originalImage] as? UIImage { + parent.selectedImage = image + } + parent.presentationMode.wrappedValue.dismiss() + } + } +} + +//struct CameraPicker: UIViewControllerRepresentable { +// +// var sourceType: UIImagePickerController.SourceType = .photoLibrary +// +// @Binding var selectedImage: [UIImage] +// @Environment(\.presentationMode) private var presentationMode +// +// func makeUIViewController(context: UIViewControllerRepresentableContext) -> UIImagePickerController { +// +// let imagePicker = UIImagePickerController() +// imagePicker.allowsEditing = false +// imagePicker.sourceType = sourceType +// imagePicker.delegate = context.coordinator +// +// return imagePicker +// } +// +// func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext) { +// +// } +// +// func makeCoordinator() -> Coordinator { +// Coordinator(self) +// } +// +// final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate { +// +// var parent: CameraPicker +// +// init(_ parent: CameraPicker) { +// self.parent = parent +// } +// +// func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { +// +// if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage { +// parent.selectedImage.append(image) +// } +// +// parent.presentationMode.wrappedValue.dismiss() +// } +// } +//} +// +//struct ImagePicker: UIViewControllerRepresentable { +// func makeCoordinator() -> Coordinator { +// return ImagePicker.Coordinator(self) +// } +// +// +// @Binding var images: [UIImage] +// @Binding var picker: Bool +// +// func makeUIViewController(context: Context) -> PHPickerViewController { +// var config = PHPickerConfiguration() +// config.filter = .images +// config.selectionLimit = 0 +// let picker = PHPickerViewController(configuration: config) +// picker.delegate = context.coordinator +// return picker +// } +// +// func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) { +// // nop +// } +// +// class Coordinator: NSObject, PHPickerViewControllerDelegate { +// let parent: ImagePicker +// +// init(_ parent1: ImagePicker) { +// self.parent = parent1 +// } +// +// func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { +// parent.picker.toggle() +// for img in results { +// if img.itemProvider.canLoadObject(ofClass: UIImage.self) { +// img.itemProvider.loadObject(ofClass: UIImage.self) { (image, error) in +// guard let image1 = image else { +// print(error) +// return +// } +//// DispatchQueue.main.async { +//// self.parent.images.append(image as! UIImage) +//// } +// self.parent.images.append(image as! UIImage) +// +// } +// } else { +// print("cannot be loaded") +// } +// } +// } +// } +//} + + diff --git a/App/LicensePlates/LicensePlates/Assets.xcassets/AccentColor.colorset/Contents.json b/App/LicensePlates/LicensePlates/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/App/LicensePlates/LicensePlates/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/App/LicensePlates/LicensePlates/Assets.xcassets/AppIcon.appiconset/Contents.json b/App/LicensePlates/LicensePlates/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/App/LicensePlates/LicensePlates/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/App/LicensePlates/LicensePlates/Assets.xcassets/Contents.json b/App/LicensePlates/LicensePlates/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/App/LicensePlates/LicensePlates/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/App/LicensePlates/LicensePlates/Controller/Api.swift b/App/LicensePlates/LicensePlates/Controller/Api.swift new file mode 100644 index 0000000..da326d9 --- /dev/null +++ b/App/LicensePlates/LicensePlates/Controller/Api.swift @@ -0,0 +1,96 @@ +// +// Api.swift +// LicensePlates +// +// Created by Maciej Czajka on 25/01/2023. +// + +import Foundation +import Alamofire +import SwiftUI + +let evaluators:[String:ServerTrustEvaluating]=[ + "http://192.168.5.129:8080":DisabledTrustEvaluator() +] +let manager=ServerTrustManager(evaluators: evaluators) +let session=Session.init(serverTrustManager:manager) + +class Api { + + func getResponse(completion: @escaping(Bool?) -> Void) { + session.request("http://192.168.5.129:8080/test",method:.get, encoding: JSONEncoding.default).response { response in + switch response.result { + case.success: + if response.response?.statusCode == 200 { + let jsonData = response.data + do { + let allReplies = try JSONDecoder().decode(RepsonseItem.self, from: jsonData!) + ResponseData.shared.str = allReplies.str + completion(true) + } catch { + print("Error: \(error)") + completion(false) + } + } + else { completion(false) } + case.failure: + completion(false) + } + } + } + + func getImageResponse(image: String, completion: @escaping(Bool?) -> Void) { + let parameters = ["imageFile": image] as [String : Any] + + session.request("http://192.168.5.129:8080/detectLicense", method: .post, parameters: parameters as Parameters, encoding: JSONEncoding.default, headers: nil) + .response { response in + switch response.result { + case.success: + if response.response?.statusCode == 200 { + var responseData = "" + do { + responseData = try JSONDecoder().decode(String.self, from: response.data!) + if responseData != "None" { + responseData = responseData.replacingOccurrences(of: "b'", with: "") + responseData = String(responseData.replacingOccurrences(of: "\'", with: "")) + responseData = responseData.fixedBase64Format + + let myUIImage = responseData.imageFromBase64 + + let image = Image(uiImage: myUIImage!) + ResponseData.shared.imageData = image + PlatesList.shared.imagesList.append(LicensePlateItem(image: image)) + + // using UserDefaults +// PlatesList.shared.imagesBase64List.append(myUIImage!.base64!) +// UserDefaults.standard.set(PlatesList.shared.imagesBase64List, forKey: "platesList") + + ResponseData.shared.uiImageData = myUIImage! + completion(true) + } else { + completion(false) + } + + } catch { + print("Error: \(error)") + completion(false) + } + + + } else { + completion(false) + } + case.failure: + completion(false) + } + } + } +} + +extension String { + var fixedBase64Format: Self { + let offset = count % 4 + guard offset != 0 else { return self } + return padding(toLength: count + 4 - offset, withPad: "=", startingAt: 0) + } +} diff --git a/App/LicensePlates/LicensePlates/Data/PlatesList.swift b/App/LicensePlates/LicensePlates/Data/PlatesList.swift new file mode 100644 index 0000000..e997de1 --- /dev/null +++ b/App/LicensePlates/LicensePlates/Data/PlatesList.swift @@ -0,0 +1,17 @@ +// +// PlatesList.swift +// LicensePlates +// +// Created by Maciej Czajka on 26/01/2023. +// + +import Foundation +import SwiftUI + +class PlatesList : ObservableObject { + @Published var imagesList: [LicensePlateItem] = [LicensePlateItem]() + // using UserDefaults +// @Published var imagesBase64List: [String] = UserDefaults.standard.stringArray(forKey: "platesList") ?? [String]() + + static let shared = PlatesList() +} diff --git a/App/LicensePlates/LicensePlates/Data/ResponseData.swift b/App/LicensePlates/LicensePlates/Data/ResponseData.swift new file mode 100644 index 0000000..d351bcc --- /dev/null +++ b/App/LicensePlates/LicensePlates/Data/ResponseData.swift @@ -0,0 +1,17 @@ +// +// ResponseData.swift +// LicensePlates +// +// Created by Maciej Czajka on 25/01/2023. +// + +import Foundation +import SwiftUI + +class ResponseData: ObservableObject { + @Published var str: String = "" + @Published var imageData: Image = Image(systemName: "trash") + @Published var uiImageData: UIImage = UIImage() + + static let shared = ResponseData() +} diff --git a/App/LicensePlates/LicensePlates/Data/WaitData.swift b/App/LicensePlates/LicensePlates/Data/WaitData.swift new file mode 100644 index 0000000..b23c5d7 --- /dev/null +++ b/App/LicensePlates/LicensePlates/Data/WaitData.swift @@ -0,0 +1,14 @@ +// +// WaitData.swift +// LicensePlates +// +// Created by Maciej Czajka on 26/01/2023. +// + +import Foundation + +class Wait: ObservableObject { + @Published var wait: Bool = false + + static let shared = Wait() +} diff --git a/App/LicensePlates/LicensePlates/LicensePlatesApp.swift b/App/LicensePlates/LicensePlates/LicensePlatesApp.swift new file mode 100644 index 0000000..ca8e588 --- /dev/null +++ b/App/LicensePlates/LicensePlates/LicensePlatesApp.swift @@ -0,0 +1,17 @@ +// +// LicensePlatesApp.swift +// LicensePlates +// +// Created by Maciej Czajka on 25/01/2023. +// + +import SwiftUI + +@main +struct LicensePlatesApp: App { + var body: some Scene { + WindowGroup { + MenuView() + } + } +} diff --git a/App/LicensePlates/LicensePlates/Model/LicensePlateItem.swift b/App/LicensePlates/LicensePlates/Model/LicensePlateItem.swift new file mode 100644 index 0000000..540dfda --- /dev/null +++ b/App/LicensePlates/LicensePlates/Model/LicensePlateItem.swift @@ -0,0 +1,14 @@ +// +// LicensePlateItem.swift +// LicensePlates +// +// Created by Maciej Czajka on 26/01/2023. +// + +import Foundation +import SwiftUI + +struct LicensePlateItem { + var id = UUID() + var image: Image +} diff --git a/App/LicensePlates/LicensePlates/Model/ResponseItem.swift b/App/LicensePlates/LicensePlates/Model/ResponseItem.swift new file mode 100644 index 0000000..34e2ef9 --- /dev/null +++ b/App/LicensePlates/LicensePlates/Model/ResponseItem.swift @@ -0,0 +1,12 @@ +// +// responseItem.swift +// LicensePlates +// +// Created by Maciej Czajka on 25/01/2023. +// + +import Foundation + +struct RepsonseItem: Codable { + let str: String +} diff --git a/App/LicensePlates/LicensePlates/Preview Content/Preview Assets.xcassets/Contents.json b/App/LicensePlates/LicensePlates/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/App/LicensePlates/LicensePlates/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/App/LicensePlates/LicensePlates/View/ContentView.swift b/App/LicensePlates/LicensePlates/View/ContentView.swift new file mode 100644 index 0000000..b13af19 --- /dev/null +++ b/App/LicensePlates/LicensePlates/View/ContentView.swift @@ -0,0 +1,199 @@ +// +// ContentView.swift +// LicensePlates +// +// Created by Maciej Czajka on 25/01/2023. +// + +import SwiftUI +import AVFoundation + +struct ContentView: View { + + @Environment(\.presentationMode) var presentationMode + @Environment(\.colorScheme) var colorScheme + @ObservedObject var item = ResponseData.shared + @ObservedObject var wait = Wait.shared + @State private var image: Image? + @State private var showingImagePicker = false + @State private var showingCameraPicker = false + @State private var isCameraAccessEnabled = false + @State public var inputImage: UIImage? + @State private var disabledButton = true + @State private var isPresented = false + @State private var notDetected = false + @State private var selection: String? = nil + private let apiManager = Api() + + var body: some View { + ZStack { + NavigationView { + VStack { + NavigationLink(destination: ResultView(), tag: "Success", selection: $selection) { EmptyView() } + Spacer() + if image != nil { + HStack { + Spacer() + image?.resizable() + .scaledToFit() + .cornerRadius(10) + .padding(.bottom, 10) + .onTapGesture { + openCamera() + } + Image(systemName: "minus.circle") + .foregroundColor(Color(.red)) + .padding(5) + .onTapGesture { + withAnimation(.easeOut) { + image = nil + inputImage = nil + disabledButton = true + } + } + Spacer() + } + } else { + HStack { + Spacer() + VStack { + Image(systemName: "plus.circle.fill") + .font(.system(size: 40)) + .foregroundColor(Color(colorScheme == .dark ? .white : .black)) + .padding(.top, 5) + .onTapGesture { + self.showingImagePicker = true + } + .padding() + Text("Choose a photo\n from the library") + .multilineTextAlignment(.center) + + } + + Spacer() + + VStack { + Image(systemName: "camera.fill") + .font(.system(size: 40)) + .foregroundColor(Color(colorScheme == .dark ? .white : .black)) + .padding(.top, 5) + .onTapGesture { + isPresented.toggle() + } + .padding() + Text("Take a photo\n with a camera") + .multilineTextAlignment(.center) + } + + Spacer() + } + .padding() + } + + if disabledButton == false { + Button { + wait.wait = true + apiManager.getImageResponse(image: inputImage!.base64!) { res in + if res == true { + selection = "Success" + wait.wait = false + image = nil + inputImage = nil + disabledButton = true + } else { + wait.wait = false + notDetected.toggle() + image = nil + inputImage = nil + disabledButton = true + } + } + } label: { + Text("Detect license plate") + } + .disabled(disabledButton) + .padding(.horizontal, 30) + .padding(.vertical, 5) +// .background(Color(colorScheme == .dark ? .white : .black)) + .cornerRadius(40) + .foregroundColor(Color(colorScheme == .dark ? .white : .black)) + .padding(5) + .overlay(RoundedRectangle(cornerRadius: 40).stroke(Color(colorScheme == .dark ? .white : .black), lineWidth: 2.0)) + .padding() + } + Spacer() + } + .alert("Please take a photo horizontally", isPresented: $isPresented) { + Button("OK", role: .cancel) { + openCamera() + } + } + .alert("No license plate detected, find another photo", isPresented: $notDetected) { + Button("OK", role: .cancel) { + // do nothing + } + } + + + } + .sheet(isPresented: $showingImagePicker, onDismiss: loadImage) { + ImagePicker(image: self.$inputImage) + } + .sheet(isPresented: $showingCameraPicker, onDismiss: loadImage) { + CameraPicker(sourceType: .camera, selectedImage: self.$inputImage) + } + + if wait.wait { + GeometryReader { _ in + LoaderView().frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height) + } + .background(Color.black.opacity(0.45)) + .edgesIgnoringSafeArea(.all) + } + } + + } + + private func openCamera() { + if AVCaptureDevice.authorizationStatus(for: .video) == .authorized { + self.showingCameraPicker = true + } else if AVCaptureDevice.authorizationStatus(for: .video) == .notDetermined { + self.showingCameraPicker = true + } else { + self.isCameraAccessEnabled = true + } + } + + func loadImage() { + guard let inputImage = inputImage else { return } + image = Image(uiImage: inputImage) + disabledButton = false + } + + func convertImageToBase64String(inputImage: UIImage?) -> String { + guard let imageData = inputImage?.jpegData(compressionQuality: 0.3) else { return ""} + let res = imageData.base64EncodedString() + return res + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} + +extension UIImage { + var base64: String? { + self.jpegData(compressionQuality: 1)?.base64EncodedString() + } +} + +extension String { + var imageFromBase64: UIImage? { + guard let imageData = Data(base64Encoded: self, options: .ignoreUnknownCharacters) else { + return nil + } + return UIImage(data: imageData) + } +} diff --git a/App/LicensePlates/LicensePlates/View/LoaderView.swift b/App/LicensePlates/LicensePlates/View/LoaderView.swift new file mode 100644 index 0000000..470a4b3 --- /dev/null +++ b/App/LicensePlates/LicensePlates/View/LoaderView.swift @@ -0,0 +1,25 @@ +import SwiftUI + +struct LoaderView: View { + + @State var animate: Bool = false + + var body: some View { + VStack{ + Circle() + .trim(from: 0.025, to: 0.82) + .stroke(AngularGradient(gradient: .init(colors: [Color(.white), Color(.white)]), center: .center), style: StrokeStyle(lineWidth: 8, lineCap: .round)) + .frame(width: 45, height: 45) + .rotationEffect(.init(degrees: self.animate ? 360 : 0)) + .animation(Animation.linear(duration: 0.7).repeatForever(autoreverses: false)) + } + .background(Color.clear) + .onAppear { self.animate.toggle() } + } +} + +struct Loader_Previews: PreviewProvider { + static var previews: some View { + LoaderView() + } +} diff --git a/App/LicensePlates/LicensePlates/View/MenuView.swift b/App/LicensePlates/LicensePlates/View/MenuView.swift new file mode 100644 index 0000000..a5e82ea --- /dev/null +++ b/App/LicensePlates/LicensePlates/View/MenuView.swift @@ -0,0 +1,32 @@ +// +// MenuView.swift +// LicensePlates +// +// Created by Maciej Czajka on 26/01/2023. +// + +import SwiftUI + +struct MenuView: View { + var body: some View { + TabView { + ContentView() + .tabItem { + Image(systemName: "viewfinder") + Text("Photo") + } + PlatesListView() + .tabItem { + Image(systemName: "list.dash") + Text("History") + } + } + .accentColor(Color(.white)) + } +} + +struct MenuView_Previews: PreviewProvider { + static var previews: some View { + MenuView() + } +} diff --git a/App/LicensePlates/LicensePlates/View/PlatesListView.swift b/App/LicensePlates/LicensePlates/View/PlatesListView.swift new file mode 100644 index 0000000..b45a7e9 --- /dev/null +++ b/App/LicensePlates/LicensePlates/View/PlatesListView.swift @@ -0,0 +1,81 @@ +// +// CodesListView.swift +// Scanner +// +// Created by Maciej Czajka on 08/11/2021. +// + +import SwiftUI + +struct PlatesListView: View { + + @ObservedObject var imagesList = PlatesList.shared + @State var isPresented: Bool = false + + var body: some View { + NavigationView { + VStack { + if imagesList.imagesList.isEmpty { + Text("No history") + } else { + List { + ForEach(imagesList.imagesList, id: \.id) { item in + item.image + .resizable() + .scaledToFit() + .padding() + + } + } + + } + } + + // using UserDefaults +// VStack { +// if imagesList.imagesBase64List.isEmpty { +// Text("No history") +// } else { +// List { +// ForEach(imagesList.imagesBase64List, id: \.self) { item in +// Image(uiImage: item.imageFromBase64!) +// .resizable() +// .scaledToFit() +// .padding() +// +// } +// } +// +// } +// } + + // using UserDefaults +// .toolbar { +// ToolbarItem(placement: .navigationBarLeading) { +// Button { +// isPresented.toggle() +// } label: { +// Image(systemName: "trash") +// } +// } +// } + + } + // using UserDefaults +// .alert(isPresented: $isPresented, content: { +// Alert(title: Text("Do you want to delete the history?"), +// primaryButton: .default(Text("No")){ +// }, +// secondaryButton:.default(Text("Yes")) { +// UserDefaults.standard.set([String](), forKey: "platesList") +//// imagesList.imagesBase64List = UserDefaults.standard.stringArray(forKey: "platesList") ?? [String]() +// }) +// }) + } +} + +struct PlatesListView_Previews: PreviewProvider { + static var previews: some View { + PlatesListView() + } +} diff --git a/App/LicensePlates/LicensePlates/View/ResultView.swift b/App/LicensePlates/LicensePlates/View/ResultView.swift new file mode 100644 index 0000000..581a22e --- /dev/null +++ b/App/LicensePlates/LicensePlates/View/ResultView.swift @@ -0,0 +1,31 @@ +// +// ResultView.swift +// LicensePlates +// +// Created by Maciej Czajka on 26/01/2023. +// + +import SwiftUI + +struct ResultView: View { + + @ObservedObject var responseData = ResponseData.shared + @State var lastScaleValue: CGFloat = 1.0 + + var body: some View { + if responseData.imageData != Image(systemName: "trash" ) { + Spacer() + responseData.imageData + .resizable() + .scaledToFit() + .padding() + Spacer() + } + } +} + +struct ResultView_Previews: PreviewProvider { + static var previews: some View { + ResultView() + } +} diff --git a/api.py b/api.py index a0c5816..18acceb 100644 --- a/api.py +++ b/api.py @@ -1,40 +1,50 @@ # python -m pip install flask -# export FLASK_APP=main.py +# export FLASK_APP=api.py # flask run --without-threads -from flask import Flask, request +from flask import Flask, request, jsonify from yolo import YOLO +import json import yolo_video import base64 +import cv2 as cv app = Flask(__name__) """ Automatic call while FLASK init """ -yolo_model = YOLO() -# def deinit_yolo(yolo): -# yolo.close_session() + +"""API_address/ping/""" +@app.route("/ping/", methods=["GET"]) +def get_ping(id: int): + print(f'Request for PING {id=} received') + id = id + 1 + return jsonify(id), 200 + +"""API_address/test""" +@app.get("/test") +def test(): + o = {'str': 'okokok'} + return jsonify(o), 200 """API_address/detectLicense?img=""" -@app.get("/detectLicense") +@app.route("/detectLicense", methods=['POST']) def detectLicensePlate(): - # build path - image_path = request.args['img'] - # decoded_data = base64.b64decode((image_base64)) - # img_file = open('img_to_detect.png', 'wb') - # img_file.write(decoded_data) - # img_file.close() - # P8080275.JPG - # image_path = 'img_to_detect.png' - image_path = './Images/' + image_path - str = yolo_video.detect_license_plate(model=yolo_model, img_path=image_path) + yolo_model = YOLO() + res = json.loads(request.data) + print('ok') - if not base64: - return { - 'str': ["None"], - }, 200 + # decoded data + decoded_data = base64.b64decode(res["imageFile"]) + img_file = open('base64.png', 'wb') + img_file.write(decoded_data) + img_file.close() + image_path = r'base64.png' + base64_b = yolo_video.detect_license_plate(model=yolo_model, img_path=image_path, i=0) + print(type(str(base64_b))) + res = {'str': str(base64_b)} + return jsonify(str(base64_b)), 200 + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=8080) - else: - return { - 'str': str, - }, 200 \ No newline at end of file diff --git a/yolo_video.py b/yolo_video.py index e795498..4b3bbe8 100644 --- a/yolo_video.py +++ b/yolo_video.py @@ -86,33 +86,15 @@ def detect_img(yolo, img_path, j): y2 = prediction[2][1] w = abs(x1 - x2) h = abs(y1 - y2) - # print(pred) - # print(f'x1: {x1}, x2: {x2}, y1: {y1}, y2: {y2}, w: {w}, h: {h}') img = processed_image[y1:y1 + h, x1:x1 + w] img = deskew(img) - # gray_image = cv.cvtColor(robot_img, cv.COLOR_BGR2GRAY) - # # gray_image = cv.bilateralFilter(gray_image, 11, 17, 17) - # gaussian_blur = cv.GaussianBlur(gray_image, (9, 9), 0) - # edged = cv.Canny(gaussian_blur, 255, 255) - # - # image_file = './img0.png' - # img = cv.imread(image_file) - gray_image = grayscale(img) - thresh, im_bw = cv.threshold(gray_image, 125, 150, cv.THRESH_BINARY) #the best = 120,150; 100, 150; 150, 210 + thresh, im_bw = cv.threshold(gray_image, 125, 150, cv.THRESH_BINARY) no_noise = noise_removal(im_bw) no_borders = remove_borders(no_noise) - # blur = cv.GaussianBlur(gray_image, (3, 3), 0) - # thresh = cv.threshold(blur, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)[1] - # - # # Morph open to remove noise and invert image - # kernel = cv.getStructuringElement(cv.MORPH_RECT, (3, 3)) - # opening = cv.morphologyEx(thresh, cv.MORPH_OPEN, kernel, iterations=1) - # no_borders = 255 - no_borders - cv.imwrite(f'img/img{j}{i}.png', no_borders) text = ocr.get_text_from_image(f'img/img{j}{i}.png') if i > 0: @@ -122,35 +104,13 @@ def detect_img(yolo, img_path, j): cv.imwrite(f'final/final{j}{i}.png', res) my_string = 'ok' i += 1 - # with open("final.png", "rb") as img_file: - # my_string = base64.b64encode(img_file.read()) - # print(my_string) - + with open(f"final/final{j}{i-1}.png", "rb") as img_file: + my_string = base64.b64encode(img_file.read()) return my_string - # text_file = open("base64.txt", "w") - # text_file.write(str(my_string)) - # text_file.close() - - # decoded data - # decoded_data = base64.b64decode((my_string)) - # img_file = open('base64.png', 'wb') - # img_file.write(decoded_data) - # img_file.close() - def detect_license_plate(model, img_path, i): str = detect_img(model, img_path, i) if not str: return None return str - -yolo_model = YOLO() -# for i in range(18,100): -# image_path = rf'Images/New/IMG_25{i}.jpeg' #95; 3909, 2491 -# detect_license_plate(model=yolo_model, img_path=image_path, i=i) -image_path = rf'Images/Old/IMG_7823.jpeg' #95; 3909, 2491 -detect_license_plate(model=yolo_model, img_path=image_path, i=0) -# print(ocr.get_text_from_image(f'img0.png')) -# print(ocr.keras_ocr_func()) -# print(ocr.tesseract_ocr()) \ No newline at end of file