5 - 灰度图片二值化

1、二值化的介绍

         二值,由字面意思即可得到,我们只希望图片转换为只有两种颜色的图像。因此,我们的处理方式即是对图片的像素值与阈值(分界点)进行比较,然后进行划分,此时阈值的选择就尤为重要。在进行阈值给定的时候,最为常见的有以下几种方式,人为给定阈值、大津法判断阈值、迭代法判断阈值等。在本文中,只记录大津法。

2、大津法的介绍

void binaryOtsu(uint8_t** inArray, uint32_t width, uint32_t height)
{        
    // 得到总的像素个数
    uint32_t pixelNum = width * height;
    
    // 图像直方图,统计每个像素值出现的次数
    uint32_t pixelCounts[256] = {0};
    for (uint32_t i = 0; i < height; i++) {
		for (uint32_t j = 0; j < width; j++) {
            uint8_t pixelValue = inArray[i][j];
            pixelCounts[pixelValue]++;
        }
    }

    // 计算各个像素值出现的概率        
    double p[256] = {0}, u_Sum = 0;    
    for(int i = 0; i < 256; i++) {
        p[i] = 1.0 * pixelCounts[i] / pixelNum;  
        u_Sum += p[i] * i;     
    }

    // 变量定义、计算
    int threshold = 0;                                                          
    double w0 = 0, w1 = 1, u0 = 0, u1 = 0, u_SumTemp = 0, maxVariance = 0;      
    for(int i = 0; i < 256; i++) {
        w0 += p[i];                                 // 计算前景概率,累加概率和
        u_SumTemp += p[i] * i;                   
        w1 = 1 - w0;                                // 计算背景概率
        if(w0 == 0 || w1 == 0)                      // 防止分母为 0
            continue;   
        u0 = u_SumTemp / w0;                        // 计算前景的数学期望        
        u1 = (u_Sum - u_SumTemp) / w1;              // 计算背景的数学期望

        // 计算最大类间方差
        double variance = w0 * w1 * (u1 - u0) * (u1 - u0);        
        if(variance > maxVariance)     
        {      
            maxVariance = variance;      
            threshold = i;      
        }    
    }   

    cout << "Otsu 计算所得的 threshold = " << threshold << endl;
}
  • 3、代码更改,减少数组空间与浮点型运算,经测试与 Matlab、Opencv 中的大津法算得的阈值相同。
/************************************************************
*Function:		getOtsuThreshold
*Description:	计算大津法的阈值
*Params:		inArray - 8 位灰度图片的二维数组数据
*				width - 图像宽度
*				height - 图像高度
*Return:		threshold - 大津法计算的阈值
************************************************************/
int getOtsuThreshold(uint8_t** inArray, uint32_t width, uint32_t height)
{        
    // 得到总的像素个数
    uint32_t Num = width * height;
    
    // 像素直方图,统计每个像素值出现的次数
    uint32_t pixelCounts[256] = {0};
    for (uint32_t i = 0; i < height; i++) {
		for (uint32_t j = 0; j < width; j++) {
            uint8_t pixelValue = inArray[i][j];
            pixelCounts[pixelValue]++;
        }
    }
    
    // 计算直方图的矩,图片的数学期望:E(x) = uSum / Num;
    uint32_t uSum = 0;
    for(int i = 0; i < 256; i++) {
        uSum += i * pixelCounts[i];    
    }

    // 定义变量
    int threshold = 0;                                      // 定义结果阈值
    uint32_t w0 = 0, w1 = 0;                                // 定义局部像素和,前景像素和、背景像素和,概率的变形,P(i) = w / Num
    double mu0 = 0, u0 = 0, u1 = 0, maxVariance = 0.0;      // 定义局部像素直方图的矩,前景像素数学期望、背景像素数学期望,最大类间方差
    
    // 循环遍历得到最大的类间方差
    for(int i = 0; i < 256; i++) {
        
        // 计算相关变量 
        w0 += pixelCounts[i];                   // 计算前景像素累加和,前景概率 P(i) = w / Num,实际中 Num 为 定值,不会影响最大类间方差的阈值,故省去除法过程
        w1 = Num - w0;                          // 得到背景像素累加和
        mu0 += i * pixelCounts[i];              // 计算矩
        if(w0 == 0 || w1 == 0)                  // 保证分母不为 0
            continue;
        u0 = mu0 / w0;                          // 得到前景的数学期望
        u1 = (uSum - mu0) * 1.0 / w1;           // 得到背景的数学期望

        // 计算最大类间方差
        double variance = w0 * w1 * (u1 - u0) * (u1 - u0);      // 公式计算,实际为 w0 / Num * w1  / Num * (u1 - u0) * (u1 - u0);省去了 / (Num * Num)
        if(variance > maxVariance)     
        {      
            maxVariance = variance;      
            threshold = i;      
        }         
    }

    return threshold;
}

3、代码示例

  • 1、二值化方式的枚举说明
// 二值化方式枚举
enum threshMode {
    threshBinary = 0,       /* value = value > threshold ? max_value : 0       */
    threshBinaryInv,        /* value = value > threshold ? 0 : max_value       */
    threshTrunc,            /* value = value > threshold ? threshold : value   */
    threshTozero,           /* value = value > threshold ? value : 0           */
    threshTozeroInv,        /* value = value > threshold ? 0 : value           */
    threshOtsu,             // 大津法判断阈值
};
  • 2、二值化函数接口
/************************************************************
*Function:		binary
*Description:	将 8 位灰度图片的二维数组数据二值化   
*Params:		inArray - 8 位灰度图片的二维数组数据
*				width - 图像宽度
*				height - 图像高度
*				mode - 二值化方式,枚举类型,当使用 Otsu 的时候,示例:threshOtsu + threshBinaryInv
*Return:		None
************************************************************/
void binary(uint8_t** inArray, uint32_t width, uint32_t height, int threshold, int mode)
{
	// 判断是否使用 大津法
    if(mode >= threshOtsu) {
        threshold = getOtsuThreshold(inArray, width, height);
        mode -= threshOtsu;
    }        
    
    // 遍历更改
    for (uint32_t i = 0; i < height; i++) {
		for (uint32_t j = 0; j < width; j++) {
			// 模式选择
            switch (mode)
			{
			case threshBinary:
				inArray[i][j] = (inArray[i][j] > threshold ? 255 : 0);
				break;	
            case threshBinaryInv:
                inArray[i][j] = (inArray[i][j] > threshold ? 0 : 255);
                break;
            case threshTrunc:
                inArray[i][j] = (inArray[i][j] > threshold ? threshold : inArray[i][j]);
                break;
            case threshTozero:
                inArray[i][j] = (inArray[i][j] > threshold ? inArray[i][j] : 0);
                break;
            case threshTozeroInv:
                inArray[i][j] = (inArray[i][j] > threshold ? 0 : inArray[i][j]);
                break;   
			default:
				break;
			}			
		}
	}
}
  • 3、函数调用
// 使用大津法判断阈值之后,进行 threshTozero 方式二值
binary(imgArr, width, height, 0, threshOtsu + threshTozero);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值