iOS:如何使用OpenVC库计算照片相似度

  1. 图像格式转换

    使用 UIImagecv::Mat 的转换时,注意通道顺序(iOS使用BGRA格式)。

  2. 性能优化

    • 对于移动端,可降低HOG参数复杂度(如减少方向数)。
    • 使用 @autoreleasepool 管理内存。
  3. 动态阈值

    建议根据实际数据集通过ROC曲线确定最佳阈值。

  4. 错误处理

    增加对空图像、无轮廓等异常情况的检查。

** 关键步骤:**
复制代码
1.引入OpenCV库
2.涉及C++,需要将.m文件更新为.mm文件
#pragma mark - 图像预处理
// 将UIImage转换为cv::Mat
复制代码
+ (cv::Mat)preprocessSignatureImage:(UIImage *)image {

    CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);

    CGFloat cols = image.size.width;

    CGFloat rows = image.size.height;

    cv::Mat cvMat(rows, cols, CV_8UC4); // 8-bit 4通道

    CGContextRef contextRef = CGBitmapContextCreate(

        cvMat.data, cols, rows, 8, cvMat.step[0], colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault

    );

    CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);

    CGContextRelease(contextRef);

    // 转为灰度图
    Mat gray;
    cvtColor(cvMat, gray, COLOR_BGRA2GRAY);

    // 二值化(Otsu)

    Mat binary;

    threshold(gray, binary, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);

    // 形态学去噪(开运算)

    Mat kernel = getStructuringElement(MORPH_RECT, cv::Size(3, 3));

    morphologyEx(binary, binary, MORPH_OPEN, kernel);
    // 尺寸归一化

    resize(binary, binary, cv::Size(128, 64));
    return binary;
}

mark - 特征提取

复制代码
+ (NSArray<NSNumber *> *)extractFeaturesFromImage:(cv::Mat)binaryImage {

    vector<float> features;
    // --------------------- HOG特征 ---------------------

    HOGDescriptor hog(

                      cv::Size(128, 64), // 窗口尺寸

                      cv::Size(16, 16),  // 块尺寸

                      cv::Size(8, 8),    // 块步长

                      cv::Size(8, 8),    // 胞元尺寸

        9              // 方向数

    );

    vector<float> hogFeatures;

    hog.compute(binaryImage, hogFeatures);

    features.insert(features.end(), hogFeatures.begin(), hogFeatures.end());

    

    // --------------------- 轮廓特征 ---------------------

    vector<vector<cv::Point>> contours;

    findContours(binaryImage.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

    // 添加轮廓数量

    features.push_back(contours.size());

    // 添加面积占比

    if (!contours.empty()) {

        double area = contourArea(contours[0]);

        features.push_back(area / (binaryImage.cols * binaryImage.rows));

    } else {

        features.push_back(0.0);

    }
    // 转为NSArray

    NSMutableArray *result = [NSMutableArray array];

    for (float val : features) {

        [result addObject:@(val)];
    }
    return result;
}`

#pragma mark - 相似度计算

复制代码
+ (double)cosineSimilarityBetweenFeature1:(NSArray<NSNumber *> *)feat1

                              feature2:(NSArray<NSNumber *> *)feat2 {

    if (feat1.count != feat2.count) {

        NSLog(@"特征维度不匹配");

        return -1;

    }

    double dotProduct = 0.0, norm1 = 0.0, norm2 = 0.0;

    for (NSUInteger i = 0; i < feat1.count; i++) {

        double val1 = feat1[i].doubleValue;

        double val2 = feat2[i].doubleValue;

        dotProduct += val1 * val2;

        norm1 += val1 * val1;

        norm2 += val2 * val2;
    }
    return dotProduct / (sqrt(norm1) * sqrt(norm2));
}

//比对方法

复制代码
+ (BOOL)compareSignaturesWithImage1:(UIImage *)image1 image2:(UIImage *)image2 similarityThreshold:(double)threshold{

    // 获取签名图像

    UIImage *signature1 = image1;

    UIImage *signature2 = image2;

    // 预处理

    cv::Mat binary1 = [ImageProcessor preprocessSignatureImage:signature1];

    cv::Mat binary2 = [ImageProcessor preprocessSignatureImage:signature2];
  
    // 特征提取

    NSArray *feat1 = [ImageProcessor extractFeaturesFromImage:binary1];
    NSArray *feat2 = [ImageProcessor extractFeaturesFromImage:binary2];
    // 计算相似度
    double similarity = [ImageProcessor cosineSimilarityBetweenFeature1:feat1

                                                              feature2:feat2];

    NSLog(@"签名相似度:%lf",similarity);

    // 判断结果(阈值根据实际数据调整)
    if (similarity > threshold) {
        return YES;
    } else {
        return NO;
    }

}

使用方法:

很简单,isSamePic是阈值,这个根据实际情况你可以设置动态的,这样就可以计算出来两张照片的相似度

复制代码
double threshold = 0.7;

 BOOL isSamePic =   [ImageProcessor compareSignaturesWithImage1:self.s1.image image2:self.s2.image similarityThreshold:threshold];

    if (isSamePic) {

        NSLog(@"两张签名照片可能是同一个人的签名。");

    } else {

        NSLog(@"两张签名照片可能不是同一个人的签名。");

    }
相关推荐
瓜子三百克5 小时前
CALayer的异步处理
macos·ios·cocoa
吴Wu涛涛涛涛涛Tao5 小时前
一步到位:用 Very Good CLI × Bloc × go_router 打好 Flutter 工程地基
flutter·ios
Fine姐6 小时前
传感器WSNs TheDataLinkLayer——B-MAC
macos
九丝城主6 小时前
2025使用VM虚拟机安装配置Macos苹果系统下Flutter开发环境保姆级教程--中篇
服务器·flutter·macos·vmware
呆萌的代Ma7 小时前
解决Mac上的老版本docker desktop 无法启动/启动后一直转圈/无法登陆账号的问题
macos·docker·eureka
fengyun28918 小时前
Omi录屏专家 Screen Recorder by Omi 屏幕录制Mac
macos·mac·录屏·屏幕录制
杂雾无尘8 小时前
开发者必看:如何在 iOS 应用中完美实现动态自定义字体!
ios·swift·apple
kymjs张涛10 小时前
零一开源|前沿技术周报 #6
前端·ios·harmonyos
不学会Ⅳ11 小时前
Mac M芯片搭建jdk源码环境(jdk24)
java·开发语言·macos
与火星的孩子对话1 天前
Unity进阶课程【六】Android、ios、Pad 终端设备打包局域网IP调试、USB调试、性能检测、控制台打印日志等、C#
android·unity·ios·c#·ip