自适应阈值二值化C语言代码实现

本文介绍了自适应阈值二值化在光照不均匀场景下的优势,对比了大津法二值化的结果,并提供了C语言实现自适应阈值二值化的代码示例,展示了如何通过局部像素计算得到更好的二值化图像效果。

本文重点关注自适应阈值二值化代码实现

应用背景

光照不均匀的场景,大津法计算得到的阈值,用于二值化的效果并不理想。尝试自适应阈值二值化或许可以得到效果更好的二值化图像。

效果对比

从上至下分别是原图1、灰度图、大津法二值化结果、自适应阈值二值化结果2
Snipaste_2022-12-20_22-52-00
colorful2gray
binary_otsu
binary_adpative

可以看到大津法二值化和自适应二值化有各自的特点。大津法二值化比较准确的区分了前景和背景;自适应二值化保留了较多细节。

代码实现

自适应阈值二值化C语言实现:3

#include "stdlib.h"
#include "stdint.h"

typedef uint8_t gray_t;

/**
 * @brief:   获取积分图像
 *           
 * @param:   *gray_img  灰度图像数组首地址
 * @param:   *sum       保存积分图像数组的首地址
 * @param:   width      灰度图像宽度
 * @param:   height     灰度图像高度
 *           
 * @date:    2022-12-20 created by 吉平.「集」
 */
void integral(uint8_t *gray_img, int *sum, int width, int height)
{
	for (int y = 0; y < height; y++)
	{
		// 获取行指针
		gray_t *input_row_ptr = &gray_img[y * width];
		int *output_row_ptr = &sum[(y + 1) * width + 1]; // 积分图像比原始图像行和列都要多1

		// 计算积分图像
		for (int x = 0; x < width; x++)
		{
			output_row_ptr[x] = 0;															// 清零
			output_row_ptr[x] = output_row_ptr[x - 1] + input_row_ptr[x];					// 0 + s_{y,x-1} + a_{x,y}
			output_row_ptr[x] += output_row_ptr[x - width] - output_row_ptr[x - width - 1]; // 当前列和
		}
	}
}

/**
 * @brief:   自适应阈值二值化
 *           
 * @param:   *gray_img  灰度图像数组首地址
 * @param:   width      灰度图像宽度
 * @param:   height     灰度图像高度
 *           
 *           @note 二值化会直接作用在原灰度图像上
 *           
 * @date:    2022-12-20 created by 吉平.「集」
 */
void adaptive_threshold_binaryzation(uint8_t *gray_img, int width, int height)
{
	// 自适应阈值取n*n范围的像素计算局部最优阈值,这里计算合适的n的取值
	int S = (width > height ? width : height)/8;
	// T是一个可调参数,影响阈值(进而影响二值化效果)
	float T = 0.15;

	// 定义变量保存n*n框选的图像范围及像素数量
	int s2 = S/2;
	int x1, y1, x2, y2, count;

	// 申请内存空间用于保存积分图像,积分图像比原始图像多一行一列
	int *sum = NULL;
	sum = (int *)calloc((width + 1) * (height + 1), sizeof(int)); // 申请内存空间,并对申请到的空间做零初始化

	// 确认是否申请到了内存空间
	if(sum != NULL)
	{
		integral(gray_img, sum, width, height); // 计算和获取积分图像

		// 外层循环用于遍历图像的每一行
		for (int y = 0; y < height; y++)
		{
			// 计算y轴上的框选范围
			y1 = y - s2;
			y2 = y + s2;

			// 避免框选到图像外的像素点
			y1 = y1 < 0 ? 0 : y1;
			y2 = y2 > (height - 1) ? (height - 1) : y2;

			// 获取灰度图像和积分图像的行指针(可以理解为单行图像的首个像素的地址)
			gray_t *row_ptr = &gray_img[y * width];
			int *y1_ptr = &sum[y1 * width];
			int *y2_ptr = &sum[y2 * width];

			// 内层循环用于遍历图像的每一列
			for (int x = 0; x < width; x++)
			{
				// 计算x轴上的框选范围
				x1 = x - s2;
				x2 = x + s2;

				// 避免框选到图像外的像素点
				x1 = x1 < 0 ? 0 : x1;
				x2 = x2 > (width - 1) ? (width - 1) : x2;

				// 计算框选的像素点的个数
				count = (x2 - x1) * (y2 - y1);

				// 计算局部阈值
				int summation = y2_ptr[x2] + y1_ptr[x1] - y1_ptr[x2] - y2_ptr[x1]; // 利用积分图快速求区域和
				uint8_t threshold = summation / count;

				// 二值化
				row_ptr[x] = ((int)(row_ptr[x] * count) > (int)(summation * (1.f - T))) ? 255 : 0;
			}
		}

		free(sum); // 释放申请的内存空间
		sum = NULL;
	}
	else
	{
		// do nothing
	}
}

参考链接

自适应阈值二值化算法

积分图像

Integral images in OpenCV

本文作者:吉平. 「集」,如有侵权,请联系我。

Jotting-Down_logo


  1. Two People On Mountain Cliff · Free Stock Photo ↩︎

  2. 转灰度和二值化用了 @Kyatto 制作的图形上位机 Mi-UpperMachine ↩︎

  3. 代码主要参考的是自适应阈值二值化算法 ↩︎

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值