Skip to content

Commit

Permalink
Made iOS App!!!!!!!
Browse files Browse the repository at this point in the history
  • Loading branch information
petelilley committed Nov 5, 2023
1 parent 417a5d6 commit 77d8f3a
Show file tree
Hide file tree
Showing 16 changed files with 416 additions and 11 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "thirdparty/tesseract"]
path = thirdparty/tesseract
url = https://github.com/tesseract-ocr/tesseract
[submodule "thirdparty/tesseract-ocr-ios"]
path = thirdparty/tesseract-ocr-ios
url = https://github.com/gali8/Tesseract-OCR-iOS
26 changes: 23 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,27 @@ cmake_minimum_required(VERSION 3.10)

project(UBHackingFall2023)

include_directories(${CMAKE_CURRENT_BINARY_DIR}/thirdparty/tesseract/include)
add_subdirectory(thirdparty/tesseract)
if(APPLE)
set(INSTALL_CONFIGS OFF)
set(DISABLE_TIFF ON)
set(BUILD_TRAINING_TOOLS ON)
set(DISABLE_CURL ON)
set(DISABLE_ARCHIVE ON)
endif()


if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
include_directories(thirdparty/tesseract-ocr-ios/TesseractOCR/include)
link_directories(thirdparty/tesseract-ocr-ios/TesseractOCR/lib)
else()
include_directories(${CMAKE_CURRENT_BINARY_DIR}/thirdparty/tesseract/include)
add_subdirectory(thirdparty/tesseract)
endif()

add_subdirectory(solver)
add_subdirectory(desktop)

if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
add_subdirectory(ios)
else()
add_subdirectory(desktop)
endif()
46 changes: 46 additions & 0 deletions ios/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
cmake_minimum_required(VERSION 3.10)

project(CVPuzzleSolver_iOS LANGUAGES CXX Swift)

set(APP_VERSION 1.0.0)
set(APP_VERSION_SHORT 1.0)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

if(NOT XCODE)
message(FATAL_ERROR "Must use Xcode for building iOS app")
endif()

file(GLOB_RECURSE SOURCES src/*.cpp src/*.mm src/*.swift)
# list(APPEND SOURCES include/UBHackingFall2023-Bridging-Header.h)

# UBHackingFall2023-Bridging-Header.h
# UBHackingFall2023-Briging-Header.h


set(CMAKE_Swift_FLAGS "${CMAKE_Swift_FLAGS} -import-objc-header ${CMAKE_CURRENT_SOURCE_DIR}/include/UBHackingFall2023-Bridging-Header.h")

set(APP_BUNDLE_IDENTIFIER "ubh-fall2023-puzzles")
set(APP_BUNDLE_NAME "${PROJECT_NAME}")
set(APP_VERSION "${APP_VERSION}")
set(APP_LONG_VERSION_STRING "${APP_VERSION}")
set(APP_SHORT_VERSION_STRING "${APP_VERSION_SHORT}")
set(APP_COPYRIGHT "Copyright © 2023 Peter Lilley and Sam Chen. All rights reserved.")
set(APP_APP_CATEGORY "public.app-category.games")
set(APP_BUNDLE_ICON_FILE "bundle_icon.icns")

add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${SOURCES})

target_link_libraries(${PROJECT_NAME} PUBLIC solver)
target_include_directories(${PROJECT_NAME} PUBLIC include)

set_target_properties(${PROJECT_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in" RESOURCE "${RESOURCES}")

set(RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/tesseract/tessdata/eng.traineddata)
target_sources(${PROJECT_NAME} PUBLIC ${RESOURCES})

set_source_files_properties(${RESOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)

51 changes: 51 additions & 0 deletions ios/Info.plist.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSCameraUsageDescription</key>
<string>Required for detecting sudoku puzzles!</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${PROJECT_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${APP_BUNDLE_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${APP_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${APP_SHORT_VERSION_STRING}</string>
<key>CFBundleLongVersionString</key>
<string>${APP_LONG_VERSION_STRING}</string>
<key>CFBundleVersion</key>
<string>${APP_VERSION}</string>
<key>NSHumanReadableCopyright</key>
<string>${APP_COPYRIGHT}</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UIStatusBarHidden</key>
<true/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
2 changes: 2 additions & 0 deletions ios/include/UBHackingFall2023-Bridging-Header.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

#import <solver_bridge.h>
11 changes: 11 additions & 0 deletions ios/include/solver_bridge.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <CoreGraphics/CGImage.h>

@interface PuzzleSolverBridge : NSObject

- (UIImage *) detectBoardIn: (UIImage *) image;

- (NSArray *) solvePuzzle: (UIImage *) image;

@end
148 changes: 148 additions & 0 deletions ios/src/content_view.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import SwiftUI
import AVFoundation

// Global variables... don't judge me it's a hackathon!!
var imageView = UIImageView(image: UIImage())
var puzzleSolverBridge = PuzzleSolverBridge()

class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate {
private var captureSession: AVCaptureSession = AVCaptureSession()
private let videoDataOutput = AVCaptureVideoDataOutput()

private func setupCamera() {
guard let device = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera, .builtInDualCamera, .builtInTrueDepthCamera], mediaType: .video, position: .back).devices.first else {
fatalError("No back camera device found, please make sure to run SimpleLaneDetection in an iOS device and not a simulator")
}
let cameraInput = try! AVCaptureDeviceInput(device: device)
self.captureSession.addInput(cameraInput)
}

override func viewDidLoad() {
super.viewDidLoad()
imageView.bounds = view.bounds
imageView.center = self.view.center;
imageView.contentMode = .scaleToFill
self.view.addSubview(imageView)

self.setupCamera()
self.setupVideo()
self.captureSession.startRunning()
}

func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
CVPixelBufferLockBaseAddress(imageBuffer, CVPixelBufferLockFlags.readOnly)
let baseAddress = CVPixelBufferGetBaseAddress(imageBuffer)
let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer)
let width = CVPixelBufferGetWidth(imageBuffer)
let height = CVPixelBufferGetHeight(imageBuffer)
let colorSpace = CGColorSpaceCreateDeviceRGB()

var bitmapInfo: UInt32 = CGBitmapInfo.byteOrder32Little.rawValue
bitmapInfo |= CGImageAlphaInfo.premultipliedFirst.rawValue & CGBitmapInfo.alphaInfoMask.rawValue

let context = CGContext(data: baseAddress, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo)
guard let quartzImage = context?.makeImage() else {
return
}

CVPixelBufferUnlockBaseAddress(imageBuffer, CVPixelBufferLockFlags.readOnly)
let input = UIImage(cgImage: quartzImage)

// Call the Objective-C which calls the C++!
let output = puzzleSolverBridge.detectBoard(in: input)

DispatchQueue.main.async {
imageView.image = output
}
}

private func setupVideo() {
videoDataOutput.videoSettings = [(kCVPixelBufferPixelFormatTypeKey as NSString) : NSNumber(value: kCVPixelFormatType_32BGRA)] as [String : Any]
videoDataOutput.alwaysDiscardsLateVideoFrames = true
videoDataOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "camera.frame.processing.queue"))
self.captureSession.addOutput(videoDataOutput)
guard let connection = self.videoDataOutput.connection(with: AVMediaType.video), connection.isVideoOrientationSupported else {
return
}
connection.videoOrientation = .portrait
}
}

// This is so stupid but it's required for SwiftUI... D:
struct ViewControllerRepresentable: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> ViewController {
return ViewController()
}

func updateUIViewController(_ uiViewController: ViewController, context: Context) {

}
}

enum AppState {
case camera
case solved
}


var solved = ""

struct ContentView: View {
@State private var appState = AppState.camera

var body: some View {
if (appState == AppState.camera) {
VStack {
ViewControllerRepresentable()
Button(action: {
let a = puzzleSolverBridge.solvePuzzle(imageView.image)
if (a != nil && a!.count == 81) {
appState = AppState.solved

for i in 0..<a!.count {
let v = (a![i] as! NSNumber).intValue
if (i == 80) {
solved += "\(v)"
}
else if (i % 9 == 8) {
solved += "\(v)\n"
}
else {
solved += "\(v) "
}
}
}
}) {
Text("Solve")
.foregroundColor(.white)
.padding()
}
.background(RoundedRectangle(cornerRadius: 10.0).fill(Color.blue))
.padding()
}
}
else {
VStack {
Text("Solution!")
.font(Font.title)
Text("\(solved)")
Button(action: {
appState = AppState.camera
solved = ""
}) {
Text("Done")
.foregroundColor(.white)
.padding()
}
.background(RoundedRectangle(cornerRadius: 10.0).fill(Color.blue))
.padding()

}
}
}
}

#Preview {
ContentView()
}
17 changes: 17 additions & 0 deletions ios/src/puzzles_app.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// swiftui_testApp.swift
// swiftui_test
//
// Created by Peter P Lilley III on 11/3/23.
//

import SwiftUI

@main
struct PuzzlesApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
51 changes: 51 additions & 0 deletions ios/src/solver_bridge.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#import <solver_bridge.h>
#include <opencv2/opencv.hpp>
#import <opencv2/imgcodecs/ios.h>
#import <Foundation/Foundation.h>
#include <solver/board_detection.h>
#include <solver/solver.h>

@implementation PuzzleSolverBridge

- (UIImage *) detectBoardIn: (UIImage *) image {
cv::Mat image_mat;
UIImageToMat(image, image_mat, true);
if (image_mat.empty()) {
return nullptr;
}

cv::cvtColor(image_mat, image_mat, cv::COLOR_RGBA2BGR);

cv::Mat frame = prepare_frame(image_mat);
std::array<cv::Point2f, 4> corners = find_corners(frame);
cv::line(image_mat, corners[0], corners[1], cv::Scalar(0, 255, 0), 4);
cv::line(image_mat, corners[1], corners[2], cv::Scalar(0, 255, 0), 4);
cv::line(image_mat, corners[2], corners[3], cv::Scalar(0, 255, 0), 4);
cv::line(image_mat, corners[3], corners[0], cv::Scalar(0, 255, 0), 4);


return MatToUIImage(image_mat);
}

- (NSArray *) solvePuzzle: (UIImage *) image {
cv::Mat image_mat;
UIImageToMat(image, image_mat, true);

std::optional<SudokuGrid> _grid = puzzle_solver(image_mat);
if (!_grid) {
return nullptr;
}

SudokuGrid grid = _grid.value();

NSMutableArray *result = [[NSMutableArray alloc] init];
for (int x = 0; x < 9; x++) {
for (int y = 0; y < 9; y++) {
[result addObject: [NSNumber numberWithInt: grid[x][y]]];
}
}

return result;
}

@end
22 changes: 22 additions & 0 deletions ios/src/viewfinder_view.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import SwiftUI
import AVFoundation


//struct ViewfinderView: View {
// let camera = Camera()
//
// @Published var viewfinderImage: Image?
//
// init() {
// Task {
// await updateCamera()
// }
// }
//
// func updateCamera() async {
// let stream = camera.stream.map { $0.image }
//
// for await image
// }
//
//}
Binary file added output.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 77d8f3a

Please sign in to comment.