图像增强--视网膜皮层Retinex算法(二)

视网膜皮层Retinex算法可以参考下面两篇文章

http://www.cnblogs.com/Imageshop/archive/2013/04/17/3026881.html

https://blog.csdn.net/carson2005/article/details/9502053

下面直接进行讲解基于Opencv 4.10的Retinex算法实现

/*主函数*/
int main()
{

	Mat orig;
	Mat dst;
	unsigned char * sImage, *dImage;
	int x, y;
	int nWidth, nHeight, step;

	orig = imread("test1.jpg", 1); /* 打开图像 */

	nWidth = orig.cols;
	nHeight = orig.rows;
	step = orig.cols * orig.channels();
	dst.create(Size(nWidth, nHeight), CV_8UC3);/* 创建目标图像 */

	sImage = (unsigned char*)malloc(sizeof(unsigned char)*(nHeight*nWidth * 3)); /* 创建2个图像buffer */
	dImage = (unsigned char*)malloc(sizeof(unsigned char)*(nHeight*nWidth * 3));

	/* 创建2个显示窗口,一个用于目标图像,一个用于源图像 */
	namedWindow("Original Image", WINDOW_AUTOSIZE);
	namedWindow("Result Image", WINDOW_AUTOSIZE);
	/* 取图像进行处理 */
	imshow("Original Image", orig);

	for (y = 0; y < orig.rows; y++)
	{
		uchar* data = orig.ptr<uchar>(y);
		for (x = 0; x < orig.cols; x++)
		{
			sImage[(y*nWidth + x) * 3 + 0] = data[3 * x + 0];
			sImage[(y*nWidth + x) * 3 + 1] = data[3 * x + 1];
			sImage[(y*nWidth + x) * 3 + 2] = data[3 * x + 2];
		}
	}

	/* 彩色图像增强 */
	MSRCR(sImage, nWidth, nHeight, orig.channels());

	for (y = 0; y < nHeight; y++)
	{
		uchar* data = dst.ptr<uchar>(y);
		for (x = 0; x < nWidth; x++)
		{
			data[3 * x + 0] = sImage[(y*nWidth + x) * 3 + 0];
			data[3 * x + 1] = sImage[(y*nWidth + x) * 3 + 1];
			data[3 * x + 2] = sImage[(y*nWidth + x) * 3 + 2];
		}
	}

	//显示处理图像
	imshow("Result Image", dst);
	waitKey(0);

	free(sImage);
	free(dImage);

	return 0;
}

下面的MSRCR是整个算法的和核心部分

/* 这个函数是算法的核心 */
/* (a) 在多个刻度处过滤,并将结果汇总 */
/* (b) 计算最终结果 */
void MSRCR(unsigned char * src, int width, int height, int bytes)
{
	int           scale, row, col;
	int           i, j;
	int           size;
	int           pos;
	int           channel;
	unsigned char *psrc = NULL;              /* SRC图的备份指针 */
	float         *dst = NULL;                     /* 算法的浮动缓冲区 */
	float         *pdst = NULL;                    /* 浮点缓冲区的备份指针 */
	float         *in, *out;
	int           channelsize;            /* 一个通道的浮动内存缓存 */
	float         weight;
	gauss3_coefs  coef;
	float         mean, var;
	float         mini, range, maxi;
	float         alpha;
	float         gain;
	float         offset;


	/* 分配算法所需的所有内存 */
	size = width * height * bytes;
	dst = (float *)malloc(size * sizeof(float));
	if (dst == NULL)
	{
		printf("Failed to allocate memory");
		return;
	}
	memset(dst, 0, size * sizeof(float));

	channelsize = (width * height);
	in = (float *)malloc(channelsize * sizeof(float));
	if (in == NULL)
	{
		free(dst);
		printf("Failed to allocate memory");
		return; /* 判断输入*/
	}

	out = (float *)malloc(channelsize * sizeof(float));
	if (out == NULL)
	{
		free(in);
		free(dst);
		printf("Failed to allocate memory");
		return; /* 判断输出 */
	}

	/* 根据过滤器数量及其分布计算过滤尺度 */
	retinex_scales_distribution(RetinexScales, rvals.nscales, rvals.scales_mode, rvals.scale);

	/* 根据不同的尺度数进行过滤 */
	/* 根据一个特定的重量总结各种过滤器的结果(这里等同于所有过滤器)*/
	weight = 1.0f / (float)rvals.nscales;

	/* 根据所选的尺度,递归滤波算法需要不同的系数(~=高斯标准偏差)*/
	pos = 0;
	for (channel = 0; channel < 3; channel++)
	{
		for (i = 0, pos = channel; i < channelsize; i++, pos += bytes)
		{
			/* 0-255 => 1-256 */
			in[i] = (float)(src[pos] + 1.0);
		}
		//对原始图像进行每个尺度的高斯模糊
		for (scale = 0; scale < rvals.nscales; scale++)
		{
			compute_coefs3(&coef, RetinexScales[scale]);
			/* 过滤(平滑)高斯递归 */

			/* 先筛选行 */
			for (row = 0; row < height; row++)
			{
				pos = row * width;
				gausssmooth(in + pos, out + pos, width, 1, &coef);
			}

			memcpy(in, out, channelsize * sizeof(float));
			memset(out, 0, channelsize * sizeof(float));

			/* 过滤(平滑)高斯递归 */

			/* 选择列 */
			for (col = 0; col < width; col++)
			{
				pos = col;
				gausssmooth(in + pos, out + pos, height, width, &coef);
			}

			/* 汇总过滤的值 */

			//对每个尺度下进行累加计算  Log[R(x,y)] =  Log[R(x,y)] + Weight(i)* ( Log[Ii(x,y)]-Log[Li(x,y)]);
			//其中Weight(i)表示每个尺度对应的权重,要求各尺度权重之和必须为1,经典的取值为等权重
			for (i = 0, pos = channel; i < channelsize; i++, pos += bytes)
			{
				dst[pos] += weight * (float)(log(src[pos] + 1.0f) - log(out[i]));
			}
		}
	}
	free(in);
	free(out);

	/* 最终计算原始值和累积滤波器值 */
	/*   参数增益、alpha和offset是常量 */

	/* Ci(x,y)=log[a Ii(x,y)]-log[ Ei=1-s Ii(x,y)] */
	alpha = 128.0f;
	gain = 1.0f;
	offset = 0.0f;

	for (i = 0; i < size; i += bytes)
	{
		float logl;

		psrc = src + i;
		pdst = dst + i;

		logl = (float)log((float)psrc[0] + (float)psrc[1] + (float)psrc[2] + 3.0f);

		pdst[0] = gain * ((float)(log(alpha * (psrc[0] + 1.0f)) - logl) * pdst[0]) + offset;
		pdst[1] = gain * ((float)(log(alpha * (psrc[1] + 1.0f)) - logl) * pdst[1]) + offset;
		pdst[2] = gain * ((float)(log(alpha * (psrc[2] + 1.0f)) - logl) * pdst[2]) + offset;
	}

	/* 根据一阶和二阶的统计数据调整颜色的动态 */
	/* 使用方差可以控制颜色的饱和度 */
	pdst = dst;

	//计算RGB各通道数据的均值Mean和均方差Var
	compute_mean_var(pdst, &mean, &var, size, bytes);
	mini = mean - rvals.cvar*var;
	maxi = mean + rvals.cvar*var;
	range = maxi - mini;

	if (!range) range = 1.0;

	//对Log[R(x,y)]的每一个值,进行线性映射
	for (i = 0; i < size; i += bytes)
	{
		psrc = src + i;
		pdst = dst + i;

		for (j = 0; j < 3; j++)
		{
			float c = 255 * (pdst[j] - mini) / range;

			psrc[j] = (unsigned char)clip(c, 0, 255);
		}
	}

	free(dst);
}

计算期望分布的尺度

void retinex_scales_distribution(float* scales, int nscales, int mode, int s)
{
	if (nscales == 1)
	{
		/* 一个参数选择中等尺度 */
		scales[0] = (float)s / 2;
	}
	else if (nscales == 2)
	{
		/* 两个参数选择中和大尺度*/
		scales[0] = (float)s / 2;
		scales[1] = (float)s;
	}
	else
	{
		float size_step = (float)s / (float)nscales;
		int   i;

		/* 按照处理模式进行处理 */
		switch (mode)
		{
		case RETINEX_UNIFORM:
			for (i = 0; i < nscales; ++i)
				scales[i] = 2.0f + (float)i * size_step;
			break;

		case RETINEX_LOW:
			size_step = (float)log(s - 2.0f) / (float)nscales;
			for (i = 0; i < nscales; ++i)
				scales[i] = 2.0f + (float)pow(10, (i * size_step) / log(10));
			break;

		case RETINEX_HIGH:
			size_step = (float)log(s - 2.0) / (float)nscales;
			for (i = 0; i < nscales; ++i)
				scales[i] = s - (float)pow(10, (i * size_step) / log(10));
			break;

		default:
			break;
		}
	}
}

计算一次平均值和方差

void compute_mean_var(float *src, float *mean, float *var, int size, int bytes)
{
	float vsquared;
	int i, j;
	float *psrc;

	vsquared = 0.0f;
	*mean = 0.0f;
	for (i = 0; i < size; i += bytes)
	{
		psrc = src + i;
		for (j = 0; j < 3; j++)
		{
			/* 平均值 */
			*mean += psrc[j];
			/* 方差 */
			vsquared += psrc[j] * psrc[j];
		}
	}

	*mean /= (float)size; /* mean */
	vsquared /= (float)size; /* mean (x^2) */
	*var = (vsquared - (*mean * *mean));
	*var = (float)sqrt(*var); /* var */
}

高斯模糊的快速计算 

关于高斯模糊如果有不清楚的可以参考这篇文章

https://www.cnblogs.com/invisible2/p/9177018.html

void compute_coefs3(gauss3_coefs * c, float sigma)
{

	float q, q2, q3;

	q = 0;

	if (sigma >= 2.5f)
	{
		q = 0.98711f * sigma - 0.96330f;
	}
	else if ((sigma >= 0.5f) && (sigma < 2.5f))
	{
		q = 3.97156f - 4.14554f * (float)sqrt((double)1 - 0.26891 * sigma);
	}
	else
	{
		q = 0.1147705018520355224609375f;
	}

	q2 = q * q;
	q3 = q * q2;
	c->b[0] = (1.57825f + (2.44413f*q) + (1.4281f *q2) + (0.422205f*q3));
	c->b[1] = ((2.44413f*q) + (2.85619f*q2) + (1.26661f *q3));
	c->b[2] = (-((1.4281f*q2) + (1.26661f *q3)));
	c->b[3] = ((0.422205f*q3));
	c->B = 1.0f - ((c->b[1] + c->b[2] + c->b[3]) / c->b[0]);
	c->sigma = sigma;
	c->N = 3;
}

高斯平滑

void gausssmooth(float *in, float *out, int size, int rowstride, gauss3_coefs *c)
{

	int i, n, bufsize;
	float *w1, *w2;

	/* forward pass */
	bufsize = size + 3;
	size -= 1;
	w1 = (float *)malloc(bufsize * sizeof(float));
	w2 = (float *)malloc(bufsize * sizeof(float));
	w1[0] = in[0];
	w1[1] = in[0];
	w1[2] = in[0];
	for (i = 0, n = 3; i <= size; i++, n++)
	{
		w1[n] = (float)(c->B*in[i*rowstride] +
			((c->b[1] * w1[n - 1] +
				c->b[2] * w1[n - 2] +
				c->b[3] * w1[n - 3]) / c->b[0]));
	}

	/* backward pass */
	w2[size + 1] = w1[size + 3];
	w2[size + 2] = w1[size + 3];
	w2[size + 3] = w1[size + 3];
	for (i = size, n = i; i >= 0; i--, n--)
	{
		w2[n] = out[i * rowstride] = (float)(c->B*w1[n] +
			((c->b[1] * w2[n + 1] +
				c->b[2] * w2[n + 2] +
				c->b[3] * w2[n + 3]) / c->b[0]));
	}

	free(w1);
	free(w2);
}

输入的原图

经过retinex算法处理的图,可以看到原图几乎什么都看不清,但是retinex算法可以将图片的颜色较好的还原出来。

代码已经传到了csdn上,下载链接是

https://download.csdn.net/download/weixin_42521239/11165325

没有积分的小伙伴可以在评论中留下你的邮箱,我发给你

评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值