在 CoreModel 文件之间切换
Switch between CoreModel file
我几乎尝试了所有方法,@EnvironmentObject
、@StateObject
、全局变量等
我有一个要跟踪的枚举和 return 我拥有的模型的名称:
enum MLNameModels: String, CaseIterable {
case audi = "Audi"
case bmw = "BMW"
var mlModel: MLModel {
switch self {
case .audi:
return try! Audi(configuration: MLModelConfiguration()).model
case .bmw:
return try! BMW(configuration: MLModelConfiguration()).model
}
}
}
我正在使用,并且在环顾四周时尝试使用实时检测,并且有一个 CameraView
来处理视图的所有设置,甚至是 Vision 框架。
struct CameraView : UIViewControllerRepresentable {
func makeUIViewController(context: UIViewControllerRepresentableContext<CameraView>) -> UIViewController {
let controller = CameraViewController()
return controller
}
func updateUIViewController(_ uiViewController: CameraView.UIViewControllerType, context: UIViewControllerRepresentableContext<CameraView>) { }
}
我的 CameraViewController 中有一行设置对象检测:
var objectDetector = Object_Detector(modelWithName: "audi")
我知道它说的是奥迪,因为我刚刚恢复到正常工作状态。
对象检测器内部 class 如下:
class Object_Detector {
// MARK: Properties
var requests = [VNRequest]()
var boundingBox = CGRect()
var objectType: ObservationTypeEnum?
var firstObservation = VNRecognizedObjectObservation()
init(modelWithName modelName: String) {
self.setupModel(withFilename: modelName)
}
// MARK: Methods
private func setupModel(withFilename modelName: String) {
// Get model URL
guard let modelURL = Bundle.main.url(forResource: modelName, withExtension: "mlmodelc") else {
NSLog("Error: Unable to find model with name\(modelName), in \(Bundle.main.bundlePath)")
return
}
// Create desired model
guard let model = try? VNCoreMLModel(for: MLModel(contentsOf: modelURL)) else {
NSLog("Error: Failed to create model->line:\(#line)")
return
}
// Perform a request using ML Model
let objectRecognizerRequests = VNCoreMLRequest(model: model) { (request, err) in
if let error = err {
NSLog("Error: \(error.localizedDescription)")
return
}
// Get observation results
guard let results = request.results as? [VNRecognizedObjectObservation] else {
NSLog("Error: Failed to extract request results as [VNRecognizedObjectObservation]")
return
}
// Get first observation result (one with the greatest confidence)
guard let firstResult = results.first else { return }
self.firstObservation = firstResult
self.objectType = ObservationTypeEnum(fromRawValue: firstResult.labels.first!.identifier)
self.boundingBox = firstResult.boundingBox
}
// Save requests
self.requests = [objectRecognizerRequests]
}
}
我已经测试过是否发生变化,甚至在 select 一个选项后调用了 setUpModel()
函数,这确实表明 modelName 参数由于某种原因已经更新了我的应用程序似乎立即转到最后一个型号,在本例中为 BMW。
我首先提示用户select一个制造商,然后之后,似乎没有任何效果。仅当我对值进行硬编码时才有效,如上所示。
澄清一下,我不想合并结果等。我只是想让应用程序知道用户使用了哪个制造商select,然后抓取相应的型号并继续识别。
编辑 - 用户提示
加载应用程序时,会出现 sheet 遍历所有模型案例,希望它对您有所帮助:
public var selectedModelName = String()
struct ContentView: View {
@State private var isPresented: Bool = false
var model = MLNameModels.allCases
var body: some View {
ZStack(alignment: .top) {
if selectedModelName.isEmpty {
CameraView().edgesIgnoringSafeArea(.all)
}
VStack(alignment: .leading){
Spacer()
HStack {
Button {
isPresented = true
print("Tapped")
} label: {
Image(systemName: "square.and.arrow.up")
.resizable()
.frame(width: 24, height: 30)
.padding()
.background(Color.secondary.clipShape(Circle()))
}
Spacer()
}
}.padding()
}
.slideOverCard(isPresented: $isPresented) {
VStack {
ForEach(model, id: \.self) { modelName in
Text(modelName.rawValue)
.onTapGesture {
selectedModelName = modelName.rawValue
isPresented = false
}
}
}
.frame(width: UIScreen.main.bounds.width * 0.85)
}
.onAppear {
if selectedModelName.isEmpty {
isPresented = true
}
}
}
}
因此,我声明:
var objectDetector = Object_Detector(modelWithName: selectedModelName)
但是好像只能加载另一个模型,根本无法切换模型。
这是我的 CameraViewController:
class CameraViewController : UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate {
var bufferSize: CGSize = .zero
var rootLayer: CALayer! = nil
private var detectionOverlay: CALayer! = nil
private let session = AVCaptureSession()
private let videoDataOutput = AVCaptureVideoDataOutput()
private let videoOutputQueue = DispatchQueue(label: "Video_Output")
private var previewLayer: AVCaptureVideoPreviewLayer! = nil
// Initializing Model
var objectDetector = Object_Detector(modelWithName: selectedModelName)
override func viewDidLoad() {
super.viewDidLoad()
loadCamera()
setupLayers()
updateLayerGeometry()
self.session.startRunning()
}
func loadCamera() {
guard let videoDevice = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: .back).devices.first else { return }
guard let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice) else {
print("NO CAMERA DETECTED")
return
}
// Begin session config
self.session.beginConfiguration()
self.session.sessionPreset = .hd1920x1080
guard self.session.canAddInput(videoDeviceInput) else {
NSLog("Could not add video device input to the session")
self.session.commitConfiguration()
return
}
// Add video input
self.session.addInput(videoDeviceInput)
if session.canAddOutput(self.videoDataOutput) {
// Add a video data output
self.session.addOutput(videoDataOutput)
videoDataOutput.alwaysDiscardsLateVideoFrames = true
videoDataOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)]
videoDataOutput.setSampleBufferDelegate(self, queue: self.videoOutputQueue)
} else {
NSLog("Could not add video data output to the session")
session.commitConfiguration()
return
}
guard let captureConnection = videoDataOutput.connection(with: .video) else { return }
// Always process the frames
captureConnection.isEnabled = true
do {
try videoDevice.lockForConfiguration()
let dimensions = CMVideoFormatDescriptionGetDimensions((videoDevice.activeFormat.formatDescription))
// Read frame dimensions
self.bufferSize.width = CGFloat(dimensions.width)
self.bufferSize.height = CGFloat(dimensions.height)
videoDevice.unlockForConfiguration()
} catch {
NSLog(error.localizedDescription)
}
// Save session config
session.commitConfiguration()
previewLayer = AVCaptureVideoPreviewLayer(session: session)
previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
rootLayer = view.layer
previewLayer.frame = rootLayer.bounds
rootLayer.addSublayer(previewLayer)
}
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
// Get buffer with image data
guard let buffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
NSLog("Error: Failed to get image buffer->\(#line)")
return
}
// Get device orientation
let deviceOrientation = self.exifOrientationFromDeviceOrientation()
// Create an image request handler
let requestHandler = VNImageRequestHandler(cvPixelBuffer: buffer, orientation: deviceOrientation, options: [:])
do {
try requestHandler.perform(self.objectDetector.requests)
// Adding bounding box
let boundingBox = self.objectDetector.boundingBox
let objectType = self.objectDetector.objectType
if !boundingBox.isEmpty && objectType != nil {
DispatchQueue.main.async {
CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
self.detectionOverlay.sublayers = nil
let objectBounds = VNImageRectForNormalizedRect(boundingBox, Int(self.bufferSize.width), Int(self.bufferSize.height))
let shapeLayer = self.createBoundingBox(withBounds: objectBounds)
let textLayer = self.createTextBox(withBounds: objectBounds)
shapeLayer.addSublayer(textLayer)
self.detectionOverlay.addSublayer(shapeLayer)
self.updateLayerGeometry()
CATransaction.commit()
}
}
} catch {
NSLog("Error: Unable to perform requests")
}
}
func createBoundingBox(withBounds bounds: CGRect) -> CALayer {
let shapeLayer = CALayer()
let borderColor = self.objectDetector.objectType?.getColor()
shapeLayer.bounds = bounds
shapeLayer.position = CGPoint(x: bounds.midX, y: bounds.midY)
shapeLayer.name = "Found Object"
shapeLayer.borderColor = borderColor
shapeLayer.borderWidth = 2.5
shapeLayer.cornerRadius = 5.0
return shapeLayer
}
func createTextBox(withBounds bounds: CGRect) -> CATextLayer {
let textLayer = CATextLayer()
textLayer.name = "Object Label"
let formattedString = NSMutableAttributedString(string: String(format: "\(self.objectDetector.firstObservation.labels[0].identifier)"))
let backgroundColor = UIColor(cgColor: self.objectDetector.objectType!.getColor())
let largeFont = UIFont(name: "AvenirNext-Medium", size: 40.0)!
formattedString.addAttributes([NSAttributedString.Key.font: largeFont, NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.backgroundColor: backgroundColor], range: NSRange(location: 0, length: self.objectDetector.firstObservation.labels[0].identifier.count))
textLayer.string = formattedString
textLayer.bounds = CGRect(x: 0, y: 0, width: bounds.size.height, height: 50)
textLayer.position = CGPoint(x: bounds.minX - 25, y: bounds.maxY)
textLayer.contentsScale = 2.0
textLayer.cornerRadius = 5.0
textLayer.setAffineTransform(CGAffineTransform(rotationAngle: CGFloat(.pi / 2.0)).scaledBy(x: 1.0, y: -1.0))
return textLayer
}
func setupLayers() {
detectionOverlay = CALayer()
detectionOverlay.name = "DetectionOverlay"
detectionOverlay.bounds = CGRect(x: 0.0, y: 0.0, width: bufferSize.width, height: bufferSize.height)
detectionOverlay.position = CGPoint(x: rootLayer.bounds.midX, y: rootLayer.bounds.midY)
rootLayer.addSublayer(detectionOverlay)
}
func updateLayerGeometry() {
let bounds = rootLayer.bounds
var scale: CGFloat
let xScale: CGFloat = bounds.size.width / bufferSize.height
let yScale: CGFloat = bounds.size.height / bufferSize.width
scale = fmax(xScale, yScale)
if scale.isInfinite {
scale = 1.0
}
CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
// Rotate the layer into screen orientation and scale and mirror
detectionOverlay.setAffineTransform(CGAffineTransform(rotationAngle: CGFloat(.pi / 2.0)).scaledBy(x: scale, y: -scale))
// Center the layer
detectionOverlay.position = CGPoint(x: bounds.midX, y: bounds.midY)
CATransaction.commit()
}
// Specify device orientation
private func exifOrientationFromDeviceOrientation() -> CGImagePropertyOrientation {
let curDeviceOrientation = UIDevice.current.orientation
let exifOrientation: CGImagePropertyOrientation
switch curDeviceOrientation {
case UIDeviceOrientation.portraitUpsideDown: // Device oriented vertically, home button on the top
exifOrientation = .left
case UIDeviceOrientation.landscapeLeft: // Device oriented horizontally, home button on the right
exifOrientation = .upMirrored
case UIDeviceOrientation.landscapeRight: // Device oriented horizontally, home button on the left
exifOrientation = .down
case UIDeviceOrientation.portrait: // Device oriented vertically, home button on the bottom
exifOrientation = .up
default:
exifOrientation = .up
}
return exifOrientation
}
}
正如您在 question/comments 中提到的那样,您目前只是获取初始值并基于它设置模型,以后不会响应或监听任何更改。
我无法重新创建您的所有代码,因为缺少很多类型等,但以下内容应该让您了解如何通过视图和对象完成传播状态更改。查看内联评论。
enum MLNameModels: String, CaseIterable { //simplified just for the example
case audi = "Audi"
case bmw = "BMW"
}
struct ContentView : View {
@State var model : String //@State, so that the view knows to update
var body: some View {
VStack {
CameraView(modelName: model) //note that it gets passed to CameraView here
VStack {
ForEach(MLNameModels.allCases, id: \.self) { modelName in
Text(modelName.rawValue)
.onTapGesture {
model = modelName.rawValue
}
}
}
}
}
}
struct CameraView : UIViewControllerRepresentable {
var modelName : String //gets updated when the parent state changes
func makeUIViewController(context: Context) -> CameraViewController {
return CameraViewController(modelName: modelName) //initial value
}
func updateUIViewController(_ uiViewController: CameraViewController, context: Context) {
uiViewController.modelName = modelName //gets called when modelName changes or the parent re-renders
}
}
class CameraViewController : UIViewController {
var objectDetector : Object_Detector
var modelName : String = "" {
didSet {
objectDetector.modelName = modelName //update modelName on the objectDetector when a new modelName is passed through
}
}
init(modelName: String) {
self.objectDetector = Object_Detector(modelWithName: modelName)
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class Object_Detector {
var modelName : String = "" {
didSet {
self.setupModel(withFilename: modelName) //call setupModel when there is a new modelName
}
}
init(modelWithName modelName: String) {
self.modelName = modelName
}
private func setupModel(withFilename modelName: String) {
//do setup
}
}
我几乎尝试了所有方法,@EnvironmentObject
、@StateObject
、全局变量等
我有一个要跟踪的枚举和 return 我拥有的模型的名称:
enum MLNameModels: String, CaseIterable {
case audi = "Audi"
case bmw = "BMW"
var mlModel: MLModel {
switch self {
case .audi:
return try! Audi(configuration: MLModelConfiguration()).model
case .bmw:
return try! BMW(configuration: MLModelConfiguration()).model
}
}
}
我正在使用,并且在环顾四周时尝试使用实时检测,并且有一个 CameraView
来处理视图的所有设置,甚至是 Vision 框架。
struct CameraView : UIViewControllerRepresentable {
func makeUIViewController(context: UIViewControllerRepresentableContext<CameraView>) -> UIViewController {
let controller = CameraViewController()
return controller
}
func updateUIViewController(_ uiViewController: CameraView.UIViewControllerType, context: UIViewControllerRepresentableContext<CameraView>) { }
}
我的 CameraViewController 中有一行设置对象检测:
var objectDetector = Object_Detector(modelWithName: "audi")
我知道它说的是奥迪,因为我刚刚恢复到正常工作状态。
对象检测器内部 class 如下:
class Object_Detector {
// MARK: Properties
var requests = [VNRequest]()
var boundingBox = CGRect()
var objectType: ObservationTypeEnum?
var firstObservation = VNRecognizedObjectObservation()
init(modelWithName modelName: String) {
self.setupModel(withFilename: modelName)
}
// MARK: Methods
private func setupModel(withFilename modelName: String) {
// Get model URL
guard let modelURL = Bundle.main.url(forResource: modelName, withExtension: "mlmodelc") else {
NSLog("Error: Unable to find model with name\(modelName), in \(Bundle.main.bundlePath)")
return
}
// Create desired model
guard let model = try? VNCoreMLModel(for: MLModel(contentsOf: modelURL)) else {
NSLog("Error: Failed to create model->line:\(#line)")
return
}
// Perform a request using ML Model
let objectRecognizerRequests = VNCoreMLRequest(model: model) { (request, err) in
if let error = err {
NSLog("Error: \(error.localizedDescription)")
return
}
// Get observation results
guard let results = request.results as? [VNRecognizedObjectObservation] else {
NSLog("Error: Failed to extract request results as [VNRecognizedObjectObservation]")
return
}
// Get first observation result (one with the greatest confidence)
guard let firstResult = results.first else { return }
self.firstObservation = firstResult
self.objectType = ObservationTypeEnum(fromRawValue: firstResult.labels.first!.identifier)
self.boundingBox = firstResult.boundingBox
}
// Save requests
self.requests = [objectRecognizerRequests]
}
}
我已经测试过是否发生变化,甚至在 select 一个选项后调用了 setUpModel()
函数,这确实表明 modelName 参数由于某种原因已经更新了我的应用程序似乎立即转到最后一个型号,在本例中为 BMW。
我首先提示用户select一个制造商,然后之后,似乎没有任何效果。仅当我对值进行硬编码时才有效,如上所示。
澄清一下,我不想合并结果等。我只是想让应用程序知道用户使用了哪个制造商select,然后抓取相应的型号并继续识别。
编辑 - 用户提示
加载应用程序时,会出现 sheet 遍历所有模型案例,希望它对您有所帮助:
public var selectedModelName = String()
struct ContentView: View {
@State private var isPresented: Bool = false
var model = MLNameModels.allCases
var body: some View {
ZStack(alignment: .top) {
if selectedModelName.isEmpty {
CameraView().edgesIgnoringSafeArea(.all)
}
VStack(alignment: .leading){
Spacer()
HStack {
Button {
isPresented = true
print("Tapped")
} label: {
Image(systemName: "square.and.arrow.up")
.resizable()
.frame(width: 24, height: 30)
.padding()
.background(Color.secondary.clipShape(Circle()))
}
Spacer()
}
}.padding()
}
.slideOverCard(isPresented: $isPresented) {
VStack {
ForEach(model, id: \.self) { modelName in
Text(modelName.rawValue)
.onTapGesture {
selectedModelName = modelName.rawValue
isPresented = false
}
}
}
.frame(width: UIScreen.main.bounds.width * 0.85)
}
.onAppear {
if selectedModelName.isEmpty {
isPresented = true
}
}
}
}
因此,我声明:
var objectDetector = Object_Detector(modelWithName: selectedModelName)
但是好像只能加载另一个模型,根本无法切换模型。
这是我的 CameraViewController:
class CameraViewController : UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate {
var bufferSize: CGSize = .zero
var rootLayer: CALayer! = nil
private var detectionOverlay: CALayer! = nil
private let session = AVCaptureSession()
private let videoDataOutput = AVCaptureVideoDataOutput()
private let videoOutputQueue = DispatchQueue(label: "Video_Output")
private var previewLayer: AVCaptureVideoPreviewLayer! = nil
// Initializing Model
var objectDetector = Object_Detector(modelWithName: selectedModelName)
override func viewDidLoad() {
super.viewDidLoad()
loadCamera()
setupLayers()
updateLayerGeometry()
self.session.startRunning()
}
func loadCamera() {
guard let videoDevice = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: .back).devices.first else { return }
guard let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice) else {
print("NO CAMERA DETECTED")
return
}
// Begin session config
self.session.beginConfiguration()
self.session.sessionPreset = .hd1920x1080
guard self.session.canAddInput(videoDeviceInput) else {
NSLog("Could not add video device input to the session")
self.session.commitConfiguration()
return
}
// Add video input
self.session.addInput(videoDeviceInput)
if session.canAddOutput(self.videoDataOutput) {
// Add a video data output
self.session.addOutput(videoDataOutput)
videoDataOutput.alwaysDiscardsLateVideoFrames = true
videoDataOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange)]
videoDataOutput.setSampleBufferDelegate(self, queue: self.videoOutputQueue)
} else {
NSLog("Could not add video data output to the session")
session.commitConfiguration()
return
}
guard let captureConnection = videoDataOutput.connection(with: .video) else { return }
// Always process the frames
captureConnection.isEnabled = true
do {
try videoDevice.lockForConfiguration()
let dimensions = CMVideoFormatDescriptionGetDimensions((videoDevice.activeFormat.formatDescription))
// Read frame dimensions
self.bufferSize.width = CGFloat(dimensions.width)
self.bufferSize.height = CGFloat(dimensions.height)
videoDevice.unlockForConfiguration()
} catch {
NSLog(error.localizedDescription)
}
// Save session config
session.commitConfiguration()
previewLayer = AVCaptureVideoPreviewLayer(session: session)
previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
rootLayer = view.layer
previewLayer.frame = rootLayer.bounds
rootLayer.addSublayer(previewLayer)
}
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
// Get buffer with image data
guard let buffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
NSLog("Error: Failed to get image buffer->\(#line)")
return
}
// Get device orientation
let deviceOrientation = self.exifOrientationFromDeviceOrientation()
// Create an image request handler
let requestHandler = VNImageRequestHandler(cvPixelBuffer: buffer, orientation: deviceOrientation, options: [:])
do {
try requestHandler.perform(self.objectDetector.requests)
// Adding bounding box
let boundingBox = self.objectDetector.boundingBox
let objectType = self.objectDetector.objectType
if !boundingBox.isEmpty && objectType != nil {
DispatchQueue.main.async {
CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
self.detectionOverlay.sublayers = nil
let objectBounds = VNImageRectForNormalizedRect(boundingBox, Int(self.bufferSize.width), Int(self.bufferSize.height))
let shapeLayer = self.createBoundingBox(withBounds: objectBounds)
let textLayer = self.createTextBox(withBounds: objectBounds)
shapeLayer.addSublayer(textLayer)
self.detectionOverlay.addSublayer(shapeLayer)
self.updateLayerGeometry()
CATransaction.commit()
}
}
} catch {
NSLog("Error: Unable to perform requests")
}
}
func createBoundingBox(withBounds bounds: CGRect) -> CALayer {
let shapeLayer = CALayer()
let borderColor = self.objectDetector.objectType?.getColor()
shapeLayer.bounds = bounds
shapeLayer.position = CGPoint(x: bounds.midX, y: bounds.midY)
shapeLayer.name = "Found Object"
shapeLayer.borderColor = borderColor
shapeLayer.borderWidth = 2.5
shapeLayer.cornerRadius = 5.0
return shapeLayer
}
func createTextBox(withBounds bounds: CGRect) -> CATextLayer {
let textLayer = CATextLayer()
textLayer.name = "Object Label"
let formattedString = NSMutableAttributedString(string: String(format: "\(self.objectDetector.firstObservation.labels[0].identifier)"))
let backgroundColor = UIColor(cgColor: self.objectDetector.objectType!.getColor())
let largeFont = UIFont(name: "AvenirNext-Medium", size: 40.0)!
formattedString.addAttributes([NSAttributedString.Key.font: largeFont, NSAttributedString.Key.foregroundColor: UIColor.white, NSAttributedString.Key.backgroundColor: backgroundColor], range: NSRange(location: 0, length: self.objectDetector.firstObservation.labels[0].identifier.count))
textLayer.string = formattedString
textLayer.bounds = CGRect(x: 0, y: 0, width: bounds.size.height, height: 50)
textLayer.position = CGPoint(x: bounds.minX - 25, y: bounds.maxY)
textLayer.contentsScale = 2.0
textLayer.cornerRadius = 5.0
textLayer.setAffineTransform(CGAffineTransform(rotationAngle: CGFloat(.pi / 2.0)).scaledBy(x: 1.0, y: -1.0))
return textLayer
}
func setupLayers() {
detectionOverlay = CALayer()
detectionOverlay.name = "DetectionOverlay"
detectionOverlay.bounds = CGRect(x: 0.0, y: 0.0, width: bufferSize.width, height: bufferSize.height)
detectionOverlay.position = CGPoint(x: rootLayer.bounds.midX, y: rootLayer.bounds.midY)
rootLayer.addSublayer(detectionOverlay)
}
func updateLayerGeometry() {
let bounds = rootLayer.bounds
var scale: CGFloat
let xScale: CGFloat = bounds.size.width / bufferSize.height
let yScale: CGFloat = bounds.size.height / bufferSize.width
scale = fmax(xScale, yScale)
if scale.isInfinite {
scale = 1.0
}
CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
// Rotate the layer into screen orientation and scale and mirror
detectionOverlay.setAffineTransform(CGAffineTransform(rotationAngle: CGFloat(.pi / 2.0)).scaledBy(x: scale, y: -scale))
// Center the layer
detectionOverlay.position = CGPoint(x: bounds.midX, y: bounds.midY)
CATransaction.commit()
}
// Specify device orientation
private func exifOrientationFromDeviceOrientation() -> CGImagePropertyOrientation {
let curDeviceOrientation = UIDevice.current.orientation
let exifOrientation: CGImagePropertyOrientation
switch curDeviceOrientation {
case UIDeviceOrientation.portraitUpsideDown: // Device oriented vertically, home button on the top
exifOrientation = .left
case UIDeviceOrientation.landscapeLeft: // Device oriented horizontally, home button on the right
exifOrientation = .upMirrored
case UIDeviceOrientation.landscapeRight: // Device oriented horizontally, home button on the left
exifOrientation = .down
case UIDeviceOrientation.portrait: // Device oriented vertically, home button on the bottom
exifOrientation = .up
default:
exifOrientation = .up
}
return exifOrientation
}
}
正如您在 question/comments 中提到的那样,您目前只是获取初始值并基于它设置模型,以后不会响应或监听任何更改。
我无法重新创建您的所有代码,因为缺少很多类型等,但以下内容应该让您了解如何通过视图和对象完成传播状态更改。查看内联评论。
enum MLNameModels: String, CaseIterable { //simplified just for the example
case audi = "Audi"
case bmw = "BMW"
}
struct ContentView : View {
@State var model : String //@State, so that the view knows to update
var body: some View {
VStack {
CameraView(modelName: model) //note that it gets passed to CameraView here
VStack {
ForEach(MLNameModels.allCases, id: \.self) { modelName in
Text(modelName.rawValue)
.onTapGesture {
model = modelName.rawValue
}
}
}
}
}
}
struct CameraView : UIViewControllerRepresentable {
var modelName : String //gets updated when the parent state changes
func makeUIViewController(context: Context) -> CameraViewController {
return CameraViewController(modelName: modelName) //initial value
}
func updateUIViewController(_ uiViewController: CameraViewController, context: Context) {
uiViewController.modelName = modelName //gets called when modelName changes or the parent re-renders
}
}
class CameraViewController : UIViewController {
var objectDetector : Object_Detector
var modelName : String = "" {
didSet {
objectDetector.modelName = modelName //update modelName on the objectDetector when a new modelName is passed through
}
}
init(modelName: String) {
self.objectDetector = Object_Detector(modelWithName: modelName)
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class Object_Detector {
var modelName : String = "" {
didSet {
self.setupModel(withFilename: modelName) //call setupModel when there is a new modelName
}
}
init(modelWithName modelName: String) {
self.modelName = modelName
}
private func setupModel(withFilename modelName: String) {
//do setup
}
}