Ex5:图像分割

Ex5:图像分割

完整代码见github: https://github.com/linjiafengyang/ComputerVision/tree/master/Ex5

迭代法求阈值

步骤如下:

  1. 计算灰度图的直方图分布
  2. 阈值threshold初始化为总灰度和的平均值
  3. 计算小于等于阈值threshold的灰度平均值t1
  4. 计算大于阈值threshold的灰度平均值t2
  5. 计算新阈值threshold_new = (t1 + t2) / 2
  6. 比较两个阈值,若两个阈值相等,则返回阈值threshold,否则更新阈值从第3步继续循环

关键代码如下

// 求阈值
int Iteration::iteration(const CImg<float>& image) {
    // 灰度直方图初始化为0
    for (int i = 0; i < 256; i++) {
        histogram[i] = 0;
    }
    pixelsNum = image.width() * image.height();
    // 计算灰度直方图分布,Histogram数组下标是灰度值,保存内容是灰度值对应像素点数
    cimg_forXY(image, i, j) {
        ++histogram[int(image(i, j, 0))];
    }

    threshold = 0;
    for (int i = 0; i < 256; i++) {
        threshold += i * histogram[i];
    }
    threshold /= pixelsNum; // 阈值初始化为总灰度和的平均值

    int threshold_new;
    while (1) {
        int t1 = 0, t2 = 0;
        int num1 = 0, num2 = 0;

        // 计算小于等于阈值threshold的灰度平均值t1
        for (int i = 0; i <= threshold; i++) {
            t1 += i * histogram[i];
            num1 += histogram[i];
        }
        if (num1 == 0) continue;
        t1 /= num1;

        // 计算大于阈值threshold的灰度平均值t2
        for (int i = threshold + 1; i < 256; i++) {
            t2 += i * histogram[i];
            num2 += histogram[i];
        }
        if (num2 == 0) continue;
        t2 /= num2;

        threshold_new = (t1 + t2) / 2;
        // 若两个阈值相等,则返回阈值threshold,否则更新阈值继续循环
        if (threshold == threshold_new) break;
        else threshold = threshold_new;
    }
    return threshold;
}

OSTU法求阈值

步骤如下

  1. 初始化一些参数,比如像素点总数pixelsNum等,然后计算灰度图的直方图分布
  2. 从0到255开始循环:计算前景像素点总数和前景像素总灰度和
  3. 计算前景像素平均灰度m1(前景像素点总数/前景像素总灰度和)和前景像素点数所占比例P1(前景像素点总数/像素点总数pixelsNum
  4. 计算背景像素点总数和背景像素总灰度和
  5. 计算背景像素平均灰度m2(背景像素点总数/背景像素总灰度和)和背景像素点数所占比例P2(背景像素点总数/像素点总数pixelsNum
  6. 计算当前类间方差temp_variance = P1 * P2 * (m1 - m2) * (m1 - m2),作比较更新类间方差和阈值

关键代码如下

// ostu算法求阈值
int OSTU::ostu(const CImg<float>& image) {
    variance = 0; // 类间方差初始化为0
    // 灰度直方图初始化为0
    for (int i = 0; i < 256; i++) {
        histogram[i] = 0;
    }
    pixelsNum = image.width() * image.height(); // 像素点总数
    // 计算灰度直方图分布,Histogram数组下标是灰度值,保存内容是灰度值对应像素点数
    cimg_forXY(image, i, j) {
        ++histogram[int(image(i, j, 0))];
    }

    for (int i = 0; i < 256; i++) {
        P1 = 0; P2 = 0; m1 = 0; m2 = 0;
        for (int j = 0; j <= i; j++) {
            P1 += histogram[j]; // 前景像素点总数
            m1 += j * histogram[j]; // 前景部分像素总灰度和
        }
        if (P1 == 0) continue;
        m1 /= P1; // 前景像素平均灰度
        P1 /= pixelsNum; // 前景像素点数所占比例

        for (int j = i + 1; j < 256; j++) {
            P2 += histogram[j]; // 背景像素点总数
            m2 += j * histogram[j]; // 背景部分像素总灰度和
        }
        if (P2 == 0) continue;
        m2 /= P2; // 背景像素平均灰度
        P2 /= pixelsNum; // 背景像素点数所占比例

        double temp_variance = P1 * P2 * (m1 - m2) * (m1 - m2); // 当前类间方差
        // 更新类间方差和阈值
        if (variance < temp_variance) {
            variance = temp_variance;
            threshold = i;
        }
    }
    return threshold;
}

图像分割结果

下面三张图左为原图右为分割结果图:
这里写图片描述

这里写图片描述

这里写图片描述

结果分析

其实,这两种算法都有一定的局限性。造成结果好坏的原因主要有:

  1. A4纸本身褶皱,导致有阴影形成,利用阈值法进行图像分割时会形成噪声,会被误认为是背景。
  2. 背景和前景区分不明显,即是说A4纸边缘和背景没有明显界面,如背景和A4纸一样偏白色时,此时做图像分割会非常困难。
  3. 拍照时光线也会对图像分割形成一定干扰。
  4. 算法本身的不足也对结果形成一定的影响。因为算法考虑的是全局阈值,全局阈值时针对整张图像而言,例如A4纸中的阴影(光线不足或者遮挡形成)会被误认为是小于等于阈值的背景。
  5. 根据上述几点,可以得出分割结果好的拍摄要求大体是:A4纸无折痕,无褶皱,投射到A4纸的光线要充足且均匀,即亮度要平均,不能在A4纸上形成某一块阴影,与此同时应尽量使得背景和前景区分明显,才能做一个效果比较好的图像分割。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值