You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS(Swift)中使用OpenCV处理扫描图像无清晰度提升问题

解决iOS OpenCV扫描文档清晰度无提升的问题

我仔细看了你这段处理扫描图像的OpenCV代码,没达到清晰效果的核心问题出在几个关键逻辑疏漏上,咱们一步步拆解修复:

核心问题分析

  1. 文本检测结果未实际使用:你声明了letterBBoxes1接收detectLetters的返回值,但后续全程用的是空数组bb,直接导致文本区域的mask为空,基于文本背景的高斯模型校准完全没执行,后面的增益调整等于没生效!
  2. 通道处理逻辑矛盾:代码假设输入是CV_8UC4(RGBA),但后续直接用Vec3b操作(只处理3通道),忽略Alpha通道会导致数据不匹配,处理逻辑失效。
  3. 缩放基准错误:你注释里写的是取宽高最大值作为缩放依据,但实际代码里只取了input.cols,竖屏图像会缩放错误。
  4. 缺少异常防护:计算增益时没处理v2[i]为0的情况,可能导致除以0的错误。

修改后的完整代码

+ (cv::Mat)cvMatFromUIImage3:(cv::Mat)image {
    cv::Mat input = image;
    // 修复:取宽高中的最大值作为缩放基准
    int maxdim = std::max(input.rows, input.cols);
    const int dim = 1024;
    if (maxdim > dim) {
        double scale = (double)dim / (double)maxdim;
        cv::Mat t;
        cv::resize(input, t, cv::Size(), scale, scale);
        input = t;
    }

    // 修复:统一处理RGBA转BGR,避免Alpha通道干扰
    cv::Mat inputBGR;
    if (input.type() == CV_8UC4) {
        cv::cvtColor(input, inputBGR, cv::COLOR_RGBA2BGR);
    } else if (input.type() == CV_8UC3) {
        inputBGR = input.clone();
    } else {
        CV_Error(CV_HAL_ERROR_UNKNOWN, "Input image must be 3-channel or 4-channel");
    }

    cv::Mat result = inputBGR.clone();
    cv::Mat median;
    cv::medianBlur(inputBGR, median, 5);

    cv::Mat localmax;
    cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(15, 15));
    cv::morphologyEx(median, localmax, cv::MORPH_CLOSE, kernel, cv::Point(-1, -1), 1, cv::BORDER_REFLECT101);

    // 修复:正确使用detectLetters的返回值
    std::vector<cv::Rect> bb = detectLetters(inputBGR);
    if (bb.empty()) {
        //  fallback:未检测到文本时,用全局对比度归一化提升清晰度
        cv::Mat adjusted;
        cv::normalize(inputBGR, adjusted, 0, 255, cv::NORM_MINMAX);
        return adjusted;
    }

    cv::Mat mask(inputBGR.size(), CV_8UC1, cv::Scalar(0));
    for (size_t i = 0; i < bb.size(); ++i) {
        cv::rectangle(result, bb[i], cv::Scalar(0, 0, 255), 2, 8); // 可视化文本框(可选)
        cv::rectangle(mask, bb[i], cv::Scalar(1), -1); // 创建文本区域mask
    }

    cv::Mat mean, dev;
    cv::meanStdDev(localmax, mean, dev, mask);
    if (mean.type() != CV_64FC1 || dev.type() != CV_64FC1 || mean.size() != cv::Size(1, 3) || dev.size() != cv::Size(1, 3)) {
        CV_Error(CV_HAL_ERROR_UNKNOWN, "Mean/StdDev calculation failed");
    }

    double minimum[3], maximum[3];
    for (unsigned int u = 0; u < 3; ++u) {
        minimum[u] = mean.at<double>(u) - dev.at<double>(u);
        maximum[u] = mean.at<double>(u) + dev.at<double>(u);
    }

    // 截断localmax到合理范围,用clamp简化逻辑
    for (int y = 0; y < localmax.rows; ++y) {
        for (int x = 0; x < localmax.cols; ++x) {
            cv::Vec3b &col = localmax.at<cv::Vec3b>(y, x);
            for (unsigned int u = 0; u < 3; ++u) {
                col[u] = cv::saturate_cast<unsigned char>(std::clamp(col[u], (unsigned char)minimum[u], (unsigned char)maximum[u]));
            }
        }
    }

    // 逐像素增益调整,增加除以0防护
    cv::Mat dst = inputBGR.clone();
    for (int y = 0; y < inputBGR.rows; ++y) {
        for (int x = 0; x < inputBGR.cols; ++x) {
            const cv::Vec3b &v1 = inputBGR.at<cv::Vec3b>(y, x);
            const cv::Vec3b &v2 = localmax.at<cv::Vec3b>(y, x);
            cv::Vec3b &v3 = dst.at<cv::Vec3b>(y, x);
            for (int i = 0; i < 3; ++i) {
                double gain = v2[i] == 0 ? 1.0 : 255.0 / (double)v2[i];
                v3[i] = cv::saturate_cast<unsigned char>(gain * v1[i]);
            }
        }
    }

    // 还原为原输入的通道格式
    if (image.type() == CV_8UC4) {
        cv::Mat dstRGBA;
        cv::cvtColor(dst, dstRGBA, cv::COLOR_BGR2RGBA);
        return dstRGBA;
    }
    return dst;
}

额外优化建议

  • 适配文本大小调整形态学核:如果扫描文档的文本偏小,把闭运算的核改成Size(9,9);文本偏大则用Size(21,21),提升局部最大值检测的准确性。
  • 结合自适应阈值:可以在增益调整后,对灰度图执行cv::adaptiveThreshold生成二值化图像,进一步强化文本与背景的对比度。
  • 优化detectLetters函数:确保文本检测逻辑先做灰度化、边缘检测,再筛选轮廓,这一步的准确性直接决定后续处理的效果。

内容的提问来源于stack exchange,提问作者RB RB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 15:07:49