【IOS开发】SwiftUI + OpenCV实现图片的简单处理(一)

以下是在 Xcode 中使用 OpenCV 的完整 Demo,包含多个实用的图像处理功能:

1. 项目设置和安装

使用 CocoaPods 安装 OpenCV,在Profile文件中添加:

ruby 复制代码
platform :ios, '12.0'
use_frameworks!

target 'OpenCVDemo' do
  pod 'OpenCV'
end

然后运行:

bash 复制代码
pod install

项目架构

text 复制代码
OpenCVDemo/
├── OpenCVDemo-Bridging-Header.h  
├── OpenCVDemoApp.swift
├── ContentView.swift
├── Tools/
│   ├── CVWrapper.h
│   ├── CVWrapper.mm
└── Resources/
    └── Assets.xcassets

2. 桥接头文件

Swift桥接OC,创建 Bridging-Header.h:

objective-c 复制代码
#import <opencv2/opencv.hpp>
#import <opencv2/imgcodecs/ios.h>

在项目设置中设置桥接文件路径:

Build Settings → Swift Compiler - General → Objective-C Bridging Header

3.Objective-C++ 包装器

创建 CVWrapper.h, 实现具体的方法:

objective-c 复制代码
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN
@interface CVWrapper : NSObject
// 基础图像处理
+ (UIImage *)convertToGray:(UIImage *)image;
+ (UIImage *)detectEdges:(UIImage *)image;
+ (UIImage *)applyBlur:(UIImage *)image;
+ (UIImage *)applySharpen:(UIImage *)image;

// 工具方法
+ (NSString *)openCVVersion;

@end

NS_ASSUME_NONNULL_END

创建 CVWrapper.mm

objective-c 复制代码
#import <opencv2/opencv.hpp>
#import <opencv2/imgcodecs/ios.h>
#import <opencv2/videoio/cap_ios.h>
#import <opencv2/objdetect.hpp>
#import "CVWrapper.h"

using namespace cv;
using namespace std;

@implementation CVWrapper
// MARK: - 基础图像处理

+ (UIImage *)convertToGray:(UIImage *)image {
    Mat inputImage;
    UIImageToMat(image, inputImage);
    
    Mat grayImage;
    if (inputImage.channels() == 1) {
        grayImage = inputImage;
    } else {
        cvtColor(inputImage, grayImage, COLOR_BGR2GRAY);
        cvtColor(grayImage, grayImage, COLOR_GRAY2BGR); // 转回BGR用于UIImage显示
    }
    
    return MatToUIImage(grayImage);
}

+ (UIImage *)detectEdges:(UIImage *)image {
    Mat inputImage;
    UIImageToMat(image, inputImage);
    
    Mat gray, edges;
    cvtColor(inputImage, gray, COLOR_BGR2GRAY);
    GaussianBlur(gray, gray, cv::Size(5, 5), 1.5);
    Canny(gray, edges, 50, 150);
    cvtColor(edges, edges, COLOR_GRAY2BGR);
    
    return MatToUIImage(edges);
}

+ (UIImage *)applyBlur:(UIImage *)image {
    Mat inputImage;
    UIImageToMat(image, inputImage);
    
    Mat blurred;
    GaussianBlur(inputImage, blurred, cv::Size(15, 15), 0);
    
    return MatToUIImage(blurred);
}

+ (UIImage *)applySharpen:(UIImage *)image {
    Mat inputImage;
    UIImageToMat(image, inputImage);
    
    Mat sharpened;
    Mat kernel = (Mat_<double>(3,3) <<
                 0, -1,  0,
                -1,  5, -1,
                 0, -1,  0);
    filter2D(inputImage, sharpened, -1, kernel);
    
    return MatToUIImage(sharpened);
}

// MARK: - 工具方法

+ (NSString *)openCVVersion {
    return [NSString stringWithFormat:@"OpenCV Version: %s", CV_VERSION];
}


@end

4. 界面实现

swift 复制代码
//
//  ContentView.swift
//  OpenCVDemo
//

import SwiftUI

struct ContentView: View {
    @State private var selectedTab = 0
    @State private var openCVVersion = ""
    
    var body: some View {
        TabView(selection: $selectedTab) {
            // 标签1: 图像处理
            ImageProcessingView()
                .tabItem {
                    Image(systemName: "photo")
                    Text("图像处理")
                }
                .tag(0)
            
            // 标签4: 关于
            AboutView(openCVVersion: openCVVersion)
                .tabItem {
                    Image(systemName: "info.circle")
                    Text("关于")
                }
                .tag(3)
        }
        .onAppear {
            openCVVersion = CVWrapper.openCVVersion()
        }
    }
}

// MARK: - 图像处理视图
struct ImageProcessingView: View {
    @State private var originalImage: UIImage? = UIImage(named: "sample_image")
    @State private var processedImage: UIImage?
    @State private var selectedFilter = 0
    
    let filters = [
        "原始图像",
        "灰度化",
        "边缘检测",
        "模糊处理",
        "锐化处理",
    ]
    
    var body: some View {
        NavigationView {
            VStack(spacing: 20) {
                // 图像显示区域
                HStack(spacing: 20) {
                    VStack {
                        Text("原图")
                            .font(.headline)
                        if let image = originalImage {
                            Image(uiImage: image)
                                .resizable()
                                .scaledToFit()
                                .frame(height: 200)
                                .border(Color.gray, width: 1)
                        } else {
                            RoundedRectangle(cornerRadius: 8)
                                .fill(Color.gray.opacity(0.3))
                                .frame(height: 200)
                                .overlay(Text("请选择图片"))
                        }
                    }
                    
                    VStack {
                        Text("处理后")
                            .font(.headline)
                        if let image = processedImage {
                            Image(uiImage: image)
                                .resizable()
                                .scaledToFit()
                                .frame(height: 200)
                                .border(Color.blue, width: 2)
                        } else {
                            RoundedRectangle(cornerRadius: 8)
                                .fill(Color.blue.opacity(0.3))
                                .frame(height: 200)
                                .overlay(Text("处理结果"))
                        }
                    }
                }
                .padding()
                
                // 滤镜选择
                Picker("选择滤镜", selection: $selectedFilter) {
                    ForEach(0..<filters.count, id: \.self) { index in
                        Text(filters[index]).tag(index)
                    }
                }
                .pickerStyle(SegmentedPickerStyle())
                .padding(.horizontal)
                
                // 处理按钮
                Button("应用处理") {
                    applyFilter()
                }
                .buttonStyle(.borderedProminent)
                .disabled(originalImage == nil)
                
                // 图片选择
                Button("选择图片") {
                    // 在实际应用中,这里应该使用UIImagePickerController
                    // 为演示目的,我们使用内置的示例图片
                    loadSampleImage()
                }
                .buttonStyle(.bordered)
                
                Spacer()
            }
            .navigationTitle("OpenCV 图像处理")
            .padding()
        }
    }
    
    private func applyFilter() {
        guard let image = originalImage else { return }
        
        switch selectedFilter {
        case 1:
            processedImage = CVWrapper.convert(toGray: image)
        case 2:
            processedImage = CVWrapper.detectEdges(image)
        case 3:
            processedImage = CVWrapper.applyBlur(image)
        case 4:
            processedImage = CVWrapper.applySharpen(image)
        default:
            processedImage = image
        }
    }
    
    private func loadSampleImage() {
        // 在实际应用中,这里应该从相册或相机获取图片
        // 这里我们创建一个简单的示例图片
        let renderer = UIGraphicsImageRenderer(size: CGSize(width: 300, height: 300))
        let image = renderer.image { context in
            UIColor.blue.setFill()
            context.fill(CGRect(x: 0, y: 0, width: 300, height: 300))
            
            UIColor.red.setFill()
            context.fill(CGRect(x: 50, y: 50, width: 100, height: 100))
            
            UIColor.green.setFill()
            context.fill(CGRect(x: 150, y: 150, width: 100, height: 100))
        }
        
        originalImage = UIImage(named: "sample")
        processedImage = nil
    }
}

// MARK: - 关于视图
struct AboutView: View {
    let openCVVersion: String
    
    var body: some View {
        NavigationView {
            VStack(spacing: 20) {
                Image(systemName: "camera.filters")
                    .font(.system(size: 80))
                    .foregroundColor(.blue)
                
                Text("OpenCV Swift Demo")
                    .font(.largeTitle)
                    .fontWeight(.bold)
                
                Text(openCVVersion)
                    .font(.title2)
                    .foregroundColor(.secondary)
                
                VStack(alignment: .leading, spacing: 15) {
                    FeatureRow(icon: "photo", title: "图像处理", description: "灰度化、边缘检测、模糊、锐化")
                    FeatureRow(icon: "camera", title: "实时处理", description: "相机实时OpenCV处理")
                    FeatureRow(icon: "face.smiling", title: "人脸检测", description: "基于Haar级联的人脸检测")
                    FeatureRow(icon: "square.and.arrow.down", title: "结果保存", description: "保存处理后的图片")
                }
                .padding()
                
                Spacer()
                
                Text("使用Swift和OpenCV构建")
                    .font(.caption)
                    .foregroundColor(.secondary)
            }
            .padding()
            .navigationTitle("关于")
        }
    }
}

struct FeatureRow: View {
    let icon: String
    let title: String
    let description: String
    
    var body: some View {
        HStack {
            Image(systemName: icon)
                .font(.title2)
                .foregroundColor(.blue)
                .frame(width: 40)
            
            VStack(alignment: .leading) {
                Text(title)
                    .font(.headline)
                Text(description)
                    .font(.caption)
                    .foregroundColor(.secondary)
            }
            
            Spacer()
        }
    }
}

// MARK: - 预览
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

运行效果图

图片进行灰度处理

图片进行"边缘检测"处理

图片进行"模糊处理"

图像进行"锐化处理

"

遇到的问题

1.问题一

Detected Apple 'NO' macro definition, it can cause build conflicts. Please, include this header before any Apple headers.

错误原因:

这个警告是因为OpenCV和Apple的SDK都定义了NO宏,导致冲突。OpenCV使用NO表示"Not OpenCV",而Apple的SDK使用NO作为布尔值。

解决办法:

调整头文件包含顺序,

objiective-c 复制代码
// 先包含OpenCV头文件
#import <opencv2/opencv.hpp>
#import <opencv2/imgcodecs/ios.h>
#import <opencv2/videoio/cap_ios.h>

// 然后包含Foundation
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
相关推荐
864记忆3 小时前
opencv图像预处理函数的功能与作用
人工智能·opencv·计算机视觉
2501_915106326 小时前
iOS 打包 IPA 全流程详解,签名配置、工具选择与跨平台上传实战指南
android·macos·ios·小程序·uni-app·cocoa·iphone
00后程序员张7 小时前
iOS 混淆实操指南多工具组合实现 IPA 混淆、加固与发布治理 IPA 加固
android·ios·小程序·https·uni-app·iphone·webview
2501_929157689 小时前
Switch 20.5.0系统最新PSP模拟器懒人包
android·游戏·ios·pdf
autism_cx10 小时前
TCP/IP协议栈
服务器·网络·笔记·网络协议·tcp/ip·ios·osi
非专业程序员Ping21 小时前
HarfBuzz概览
android·ios·swift·font
Antonio9151 天前
【图像处理】灰度图像与二值化
图像处理·opencv
Mrliu__1 天前
Opencv(一): 用Opencv了解图像
人工智能·opencv·计算机视觉
Daniel_Coder1 天前
iOS Widget 开发-8:手动刷新 Widget:WidgetCenter 与刷新控制实践
ios·swift·widget·1024程序员节·widgetcenter