opencv的cvtColor函数Lab转RGB源码解析及结果截断处理解决

 cvtColor( InputArray src, OutputArray dst, int code, int dstCn=0 );         

这是opencv中色彩转换函数。当由Lab转RGB时,code = COLOR_Lab2RGB,输出的RGB值域[0,1],再乘255,输出像素值即可归至[0,255]。
色彩空间转换过程中,接口函数做了截断处理,即R,G,B的值,若<0,修改为0;若>1,修改为1。opencv2.4.10源码截断处理如下所示:

#define clip(value)  value < 0.0f ? 0.0f : value > 1.0f ? 1.0f : value;
float ro = clip(C0 * x + C1 * y + C2 * z);
float go = clip(C3 * x + C4 * y + C5 * z);
float bo = clip(C6 * x + C7 * y + C8 * z);

如果转换的图像没有alpha通道,最后还要做gamma处理增强对比度:

if (gammaTab)
 {
     ro = splineInterpolate(ro * gscale, gammaTab, GAMMA_TAB_SIZE);
     go = splineInterpolate(go * gscale, gammaTab, GAMMA_TAB_SIZE);
     bo = splineInterpolate(bo * gscale, gammaTab, GAMMA_TAB_SIZE);
 }

常见的转换方法,是没有这个步骤处理的。

所以,如果不做gamma处理,用博主https://blog.csdn.net/lz0499/article/details/77345166中介绍的方法即可,思路非常清晰,代码也很容易理解。其结果与opencv不做gamma处理的结果是完全一样的。但如果要做gamma处理,同时不做截断处理,由于本人对cvtColor函数没有彻底看懂,就粗暴的把转换部分代码摘取出来,并去掉了clip处理。代码如下:

enum { LAB_CBRT_TAB_SIZE = 1024, GAMMA_TAB_SIZE = 1024 };
static ushort sRGBGammaTab_b[256], linearGammaTab_b[256];
static float sRGBGammaTab[GAMMA_TAB_SIZE*4], sRGBInvGammaTab[GAMMA_TAB_SIZE*4];
static const float GammaTabScale = (float)GAMMA_TAB_SIZE;
static float LabCbrtTab[LAB_CBRT_TAB_SIZE*4];
static const float LabCbrtTabScale = LAB_CBRT_TAB_SIZE/1.5f;

enum
{
	yuv_shift = 14,
	xyz_shift = 12,
	R2Y = 4899,
	G2Y = 9617,
	B2Y = 1868,
	BLOCK_SIZE = 256
};
#define lab_shift xyz_shift
#define gamma_shift 3
#define lab_shift2 (lab_shift + gamma_shift)

#define LAB_CBRT_TAB_SIZE_B (256*3/2*(1<<gamma_shift))
static ushort LabCbrtTab_b[LAB_CBRT_TAB_SIZE_B];

static const float XYZ2sRGB_D65[] =
{
	3.240479f, -1.53715f, -0.498535f,
	-0.969256f, 1.875991f, 0.041556f,
	0.055648f, -0.204043f, 1.057311f
};
static const float D65[] = { 0.950456f, 1.f, 1.088754f };
class CLab2RGBImpl
{
public:
    void init(int blueIdx,bool _srgb)
	{
		srgb = _srgb;//当没有alpha通道时,srgb = true
		blueInd = blueIdx;//蓝通道索引:转rgb,blueIdx = 2;转bgr,blueIdx = 0;

		initLabTabs();

		const float* _coeffs = XYZ2sRGB_D65;
		const float* _whitept = D65;

		for( int i = 0; i < 3; i++ )
		{
			coeffs[i+(blueIdx^2)*3] = _coeffs[i]*_whitept[i];
			coeffs[i+3] = _coeffs[i+3]*_whitept[i];
			coeffs[i+blueIdx*3] = _coeffs[i+6]*_whitept[i];
		}
	}
	void convert(const cv::Mat& matSrc,cv::Mat& matDst)
	{
		int i, dcn = 3;
		const float* gammaTab = srgb ? sRGBInvGammaTab : 0;
		float gscale = GammaTabScale;
		float C0 = coeffs[0], C1 = coeffs[1], C2 = coeffs[2],
			C3 = coeffs[3], C4 = coeffs[4], C5 = coeffs[5],
			C6 = coeffs[6], C7 = coeffs[7], C8 = coeffs[8];

		static const float lThresh = 0.008856f * 903.3f;
		static const float fThresh = 7.787f * 0.008856f + 16.0f / 116.0f;

		if (matSrc.ptr(0) == NULL)
		{
			return;
		}

		int nCn = matSrc.channels();
		int nWidth = matSrc.cols;
		int nHeigh = matSrc.rows;
		if (nCn != 3)
		{
			return;
		}

		if (matDst.ptr(0) == NULL)
		{
			matDst = cv::Mat(matSrc.size(),matSrc.type());
		}

		float* pSrcData = (float*)(matSrc.ptr(0));
		float* pDstData = (float*)(matDst.ptr(0));
		for (int y = 0; y < nHeigh; y++)
		{
			for (int x = 0; x < nWidth; x++)
			{
				float li = pSrcData[0];
				float ai = pSrcData[1];
				float bi = pSrcData[2];

				float y, fy;
				if (li <= lThresh)
				{
					y = li / 903.3f;
					fy = 7.787f * y + 16.0f / 116.0f;
				}
				else
				{
					fy = (li + 16.0f) / 116.0f;
					y = fy * fy * fy;
				}

				float fxz[] = { ai / 500.0f + fy, fy - bi / 200.0f };

				for (int j = 0; j < 2; j++)
					if (fxz[j] <= fThresh)
						fxz[j] = (fxz[j] - 16.0f / 116.0f) / 7.787f;
					else
						fxz[j] = fxz[j] * fxz[j] * fxz[j];


				float x = fxz[0], z = fxz[1];

				//cv源码在此做截断,即ro,go,bo:小于0赋值0,大于1赋值1
				float ro = C0 * x + C1 * y + C2 * z;
				float go = C3 * x + C4 * y + C5 * z;
				float bo = C6 * x + C7 * y + C8 * z;

				if (gammaTab) //没有alpha通道,做下面处理。相比常见的转换方法,如果没有此步骤,结果一样
				{
					ro = splineInterpolate(ro * gscale, gammaTab, GAMMA_TAB_SIZE);
					go = splineInterpolate(go * gscale, gammaTab, GAMMA_TAB_SIZE);
					bo = splineInterpolate(bo * gscale, gammaTab, GAMMA_TAB_SIZE);
				}

				pDstData[0] = ro;
				pDstData[1] = go;
				pDstData[2] = bo;

				pSrcData += 3;
				pDstData += 3;
			}
		}
	}

protected:
	static void initLabTabs()
	{
		static bool initialized = false;
		if(!initialized)
		{
			float f[LAB_CBRT_TAB_SIZE+1], g[GAMMA_TAB_SIZE+1], ig[GAMMA_TAB_SIZE+1], scale = 1.f/LabCbrtTabScale;
			int i;
			for(i = 0; i <= LAB_CBRT_TAB_SIZE; i++)
			{
				float x = i*scale;
				f[i] = x < 0.008856f ? x*7.787f + 0.13793103448275862f : cvCbrt(x);
			}
			splineBuild(f, LAB_CBRT_TAB_SIZE, LabCbrtTab);

			scale = 1.f/GammaTabScale;
			for(i = 0; i <= GAMMA_TAB_SIZE; i++)
			{
				float x = i*scale;
				g[i] = x <= 0.04045f ? x*(1.f/12.92f) : (float)pow((double)(x + 0.055)*(1./1.055), 2.4);
				ig[i] = x <= 0.0031308 ? x*12.92f : (float)(1.055*pow((double)x, 1./2.4) - 0.055);
			}
			splineBuild(g, GAMMA_TAB_SIZE, sRGBGammaTab);
			splineBuild(ig, GAMMA_TAB_SIZE, sRGBInvGammaTab);

			for(i = 0; i < 256; i++)
			{
				float x = i*(1.f/255.f);
				sRGBGammaTab_b[i] = (ushort)max((int)(255.f*(1 << gamma_shift)*(x <= 0.04045f ? x*(1.f/12.92f) : (float)pow((double)(x + 0.055)*(1./1.055), 2.4))), 0);
				linearGammaTab_b[i] = (ushort)(i*(1 << gamma_shift));
			}

			for(i = 0; i < LAB_CBRT_TAB_SIZE_B; i++)
			{
				float x = i*(1.f/(255.f*(1 << gamma_shift)));
				LabCbrtTab_b[i] = (ushort)max((int)((1 << lab_shift2)*(x < 0.008856f ? x*7.787f + 0.13793103448275862f : cvCbrt(x))), 0);
			}
			initialized = true;
		}
	}

	// computes cubic spline coefficients for a function: (xi=i, yi=f[i]), i=0..n
	template<typename _Tp> static void splineBuild(const _Tp* f, int n, _Tp* tab)
	{
		_Tp cn = 0;
		int i;
		tab[0] = tab[1] = (_Tp)0;

		for(i = 1; i < n-1; i++)
		{
			_Tp t = 3*(f[i+1] - 2*f[i] + f[i-1]);
			_Tp l = 1/(4 - tab[(i-1)*4]);
			tab[i*4] = l; tab[i*4+1] = (t - tab[(i-1)*4+1])*l;
		}

		for(i = n-1; i >= 0; i--)
		{
			_Tp c = tab[i*4+1] - tab[i*4]*cn;
			_Tp b = f[i+1] - f[i] - (cn + c*2)*(_Tp)0.3333333333333333;
			_Tp d = (cn - c)*(_Tp)0.3333333333333333;
			tab[i*4] = f[i]; tab[i*4+1] = b;
			tab[i*4+2] = c; tab[i*4+3] = d;
			cn = c;
		}
	}

	// interpolates value of a function at x, 0 <= x <= n using a cubic spline.
	template<typename _Tp> static inline _Tp splineInterpolate(_Tp x, const _Tp* tab, int n)
	{
		// don't touch this function without urgent need - some versions of gcc fail to inline it correctly
		int ix = min(max(int(x), 0), n-1);
		x -= ix;
		tab += ix*4;
		return ((tab[3]*x + tab[2])*x + tab[1])*x + tab[0];
	}

private:
	int dstcn;
	float coeffs[9];
	bool srgb;
	int blueInd;
};

使用时,首先定义类CLab2RGBImpl,然后init初始化,最后convert转换。当图像比较大,需要分块转换时,只用初始化一次。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值