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(@"两张签名照片可能不是同一个人的签名。");

    }
相关推荐
旭日跑马踏云飞21 分钟前
【Mac】实用技巧-在新窗口中打开当前文件夹
macos
先飞的笨鸟1 小时前
2026 年 Expo + React Native 项目接入微信分享完整指南
前端·ios·app
初级代码游戏1 小时前
iOS开发 SwiftUI 5 : 文本输入 密码输入 多行输入
ios·swiftui·swift
iosTiov2 小时前
ios生态的分发密钥:企业签、V3签、TF签深度解析与选型指南
安全·ios·团队开发·苹果签名·稳定
林疏safe2 小时前
CAD2026最新mac的安装教程收集免费分享
macos
Digitally3 小时前
如何高效安全地将 iPhone 与 Mac 同步
安全·macos·iphone
程序员允诺3 小时前
办公 WiFi 二次隔离下的远程桌面解决方案实践(Mac → Win11)
macos
2501_915918413 小时前
介绍如何在电脑上查看 iPhone 和 iPad 的完整设备信息
android·ios·小程序·uni-app·电脑·iphone·ipad
hudawei9963 小时前
win和Mac在创建python虚拟环境,启动环境等操作的异同
windows·python·macos·虚拟环境
晨枫阳3 小时前
iOS Universal Links配置
ios