ARGB to YCrCb

Started by
5 comments, last by GameCat 19 years, 6 months ago
Hi guys, i have here a buffer (w*h) in which every byte contains a colorvalue in ARGB-Format. Now i need a way to convert a) the whole buffer to YCrCb b) one single ARGB value to YCrCb I hope somebody can help me. Thanks, Christian
Advertisement
Some conversion code from my wavelet encoder:
/************************************************ *  Color space conversion matrix coefficients  * ************************************************//* all components are scaled up by 15 bits to	*  * yield a minimum degree of precision while	* * still using pure integer math				*/#define Yr	(short)(0.257 * 32768 + 0.5)#define Yg	(short)(0.504 * 32768 + 0.5)#define Yb	(short)(0.098 * 32768 + 0.5)#define Ur	(short)(-0.148 * 32768 + 0.5)#define Ug	(short)(-0.291 * 32768 + 0.5)#define Ub	(short)(0.439  * 32768 + 0.5)#define Vr	(short)(0.439  * 32768 + 0.5)#define Vg	(short)(-0.368 * 32768 + 0.5)#define Vb	(short)(-0.071 * 32768 + 0.5)static const short Y_COEFF[4] = {	Yb, Yg, Yr, 0x00};static const short U_COEFF[4] = {	Ub, Ug, Ur, 0x00};static const short V_COEFF[4] = {	Vb, Vg, Vr, 0x00};/* SIMD (MMX™) version */int swciRgbToYuv(swcImage* image) {		dword_ptr	src;	byte_ptr	y;	byte_ptr	u;	byte_ptr	v;	int			w, h;	if (!image || !image->rgb) {		return SWC_ERR;	}	w	= image->cols;	h	= image->rows;	src = image->rgb;		y	= image->y;	u	= image->u;	v	= image->v;	__asm {		/* push all double word registers and init FPU */		pushad		finit		/* setup pointers */		mov			esi, src		mov			edi, y		mov			ecx, u		mov			edx, v				mov			ebx, hrow_loop0:		/* loop y */		 		push		ebx		mov			ebx, w		col_loop0:		/* loop x */		push		ebx		/* ------------------------------------------------------- */		/*	 two pixels at once are processed by these routines	   */		/* ------------------------------------------------------- */		/* load two pixels */		movq		mm0, [esi]		movq		mm1, mm0		pxor		mm2, mm2		/* mm0 = 0|0|0|R1|0|G1|0|B1 */		/* mm1 = 0|0|0|R2|0|G2|0|B2 */		punpcklbw	mm0, mm2		punpckhbw	mm1, mm2		/* save unpacked 16 rgb vectors */		movq		mm3, mm0		movq		mm4, mm1		/* compute dot product for two Y components */		movq		mm5, [Y_COEFF]		pmaddwd		mm0, mm5		pmaddwd		mm1, mm5		/* shift to 15 bit precision */		psrad		mm0, 15		psrad		mm1, 15				/* tricky here - need to use mm6 as accumulator */		movq		mm6, mm0		psrlq		mm6, 32				/* add intermediate results for final Y calculation */		paddd		mm0, mm6		movd		eax, mm0		add			eax, 16				/* add intermediate results for final Y calculation (second pixel) */		movq		mm6, mm1		psrlq		mm6, 32				paddd		mm1, mm6		movd		ebx, mm1		add			ebx, 16		/* combine results from eax and ebx to one word */		shl			ebx, 8		or			eax, ebx				/* save two Y components */		mov			[edi], ax		/* process U and v components - same as above */		/* restore unpacked 16 bit vectors */		movq		mm0, mm3		movq		mm1, mm4		movq		mm5, [U_COEFF]		pmaddwd		mm0, mm5		pmaddwd		mm1, mm5		psrad		mm0, 15		psrad		mm1, 15				movq		mm6, mm0		psrlq		mm6, 32						paddd		mm0, mm6		movd		eax, mm0		add			eax, 128		movq		mm6, mm1		psrlq		mm6, 32				paddd		mm1, mm6		movd		ebx, mm1		add			ebx, 128		/* add the two components */		add			eax, ebx		/* save intermediate result */		push		eax		/* restore unpacked 16 bit vectors */		movq		mm0, mm3		movq		mm1, mm4		movq		mm5, [V_COEFF]		pmaddwd		mm0, mm5		pmaddwd		mm1, mm5		psrad		mm0, 15		psrad		mm1, 15				movq		mm6, mm0		psrlq		mm6, 32						paddd		mm0, mm6		movd		eax, mm0		add			eax, 128		movq		mm6, mm1		psrlq		mm6, 32				paddd		mm1, mm6		movd		ebx, mm1		add			ebx, 128		/* add the two components */		add			eax, ebx		/* save intermediate result */		push		eax		mov			ebx, w		/* load two pixels  from second scanline */		movq		mm0, [esi + ebx*4]		movq		mm1, mm0		pxor		mm2, mm2				/* mm0 = 0|0|0|R1|0|G1|0|B1 */		/* mm1 = 0|0|0|R2|0|G2|0|B2 */		punpcklbw	mm0, mm2		punpckhbw	mm1, mm2		/* save unpacked 16 rgb vectors */		movq		mm3, mm0		movq		mm4, mm1		/* compute dot product for second Y component */		movq		mm5, [Y_COEFF]		pmaddwd		mm0, mm5		pmaddwd		mm1, mm5		psrad		mm0, 15		psrad		mm1, 15				movq		mm6, mm0		psrlq		mm6, 32				paddd		mm0, mm6		movd		eax, mm0		add			eax, 16				movq		mm6, mm1		psrlq		mm6, 32				paddd		mm1, mm6		movd		ebx, mm1		add			ebx, 16		shl			ebx, 8		or			eax, ebx				/* save Y component */		mov			ebx, w		mov			[edi + ebx], ax		/* restore unpacked 16 bit vectors */		movq		mm0, mm3		movq		mm1, mm4		/* IMPORTANT NOTE: need to process V first, since last value on stack */		/* is intermediate result from V components!						  */		movq		mm5, [V_COEFF]		pmaddwd		mm0, mm5		pmaddwd		mm1, mm5		psrad		mm0, 15		psrad		mm1, 15				movq		mm6, mm0		psrlq		mm6, 32						paddd		mm0, mm6		movd		eax, mm0		add			eax, 128		movq		mm6, mm1		psrlq		mm6, 32				paddd		mm1, mm6		movd		ebx, mm1		add			ebx, 128		/* add the two components */		add			eax, ebx		/* restore intermediate result from last scanline */		pop			ebx		/* add current values and average them */		add			eax, ebx		sar			eax, 2		/* store V component */		mov			[edx], al		/* restore unpacked 16 bit vectors */		movq		mm0, mm3		movq		mm1, mm4		movq		mm5, [U_COEFF]		pmaddwd		mm0, mm5		pmaddwd		mm1, mm5		psrad		mm0, 15		psrad		mm1, 15				movq		mm6, mm0		psrlq		mm6, 32						paddd		mm0, mm6		movd		eax, mm0		add			eax, 128		movq		mm6, mm1		psrlq		mm6, 32				paddd		mm1, mm6		movd		ebx, mm1		add			ebx, 128		/* add the two components */		add			eax, ebx		/* restore intermediate result from last scanline */		pop			ebx		/* add current values and average them */		add			eax, ebx		sar			eax, 2		/* store U component */		mov			[ecx], al		/* increment Y,U,V target pointers */		add			edi, 2		inc			ecx		inc			edx		/* increment source pointer */		add			esi, 8		/* endloop x */		pop			ebx		sub			ebx, 2		jnz			col_loop0		/* skip to next scanline */		mov			ebx, w		add			edi, ebx		shl			ebx, 2		add			esi, ebx		/* endloop y */		pop			ebx		sub			ebx, 2		jnz			row_loop0		emms		popad	}	return SWC_OK;}int swciYuvToRgb(swcImage* image) {	dword_ptr tmp;	int		  w, h;	byte_ptr  y, u, v;		if (!image || !image->rgb) {		/* invalid params */		return SWC_ERR;	}	/* copy image properties to local variables to make them accessable from	 * within our inline assembly core	*/	tmp = image->rgb;	w = image->cols;	h = image->rows;	y = image->y;	u = image->u;	v = image->v;	__asm {		pushad				/* save all dword registers */		finit				/* initialize FPU */		/* actual MMX implementation follows here */		xor	eax, eax		/* clear eax to zero */				mov	ebx, h			/* store image height in ebx */		mov	ecx, w			/* store image width in ecx */		mov	edx, y				mov	edi, u		mov	esi, vrow_loop:		push	ebx			/* save image height - we use ebx for many things */		mov		ebx, ecx	/* setup col count */col_loop:		push	ebx			/* save col count - we mess with ebx later */		xor		ebx, ebx	/* clear all 32 bits */		/* mm0 = Bu | Gu + Gv | Rv | 0000  */		mov			al, [edi]		mov			bl, [esi]		movq		mm0, [CoefRgbU + 8 * eax]		paddw		mm0, [CoefRgbV + 8 * ebx]		/* for each U,V pair there are four Y components to process (YUV420!) */		/* first scanline */		/* mm1 = By0 | Gy0 | Ry0 | 0000, mm1 = By1 | Gy1 | Ry1 | 0000 */		mov			al, [edx]		mov			bl, [edx+1]		movq		mm1, [CoefRgbY + 8 * eax]		movq		mm2, [CoefRgbY + 8 * ebx]				/* next scanline */		/* mm3 = By2 | Gy2 | Ry2 | 0000, mm4 = By3 | Gy3 | Ry3 | 0000 */		mov			al, [edx + ecx]		mov			bl, [edx + ecx + 1]		movq		mm3, [CoefRgbY + 8 * eax]		movq		mm4, [CoefRgbY + 8 * ebx]		/* process all four pixels at one iteration */		paddw		mm1, mm0		paddw		mm2, mm0		paddw		mm3, mm0		paddw		mm4, mm0		/* divide by 64 to scale to byte values */		psraw		mm1, 6		psraw		mm2, 6		psraw		mm3, 6		psraw		mm4, 6		/* now we need to clamp values - packuswb comes in handy here:		 *	values below zero are clamped to zero, values above 255 are set to 255		 */		packuswb	mm1, mm2		packuswb	mm3, mm4		/* now store the for pixels from mm1 and mm3 into target */		mov			ebx, tmp		movntq		[ebx], mm1		movq		[ebx + 4 * ecx], mm3		/* proceed to next pixel pair */		add			tmp, 8		add			edx, 2		add			edi, 1		add			esi, 1		/* loop */		pop			ebx		sub			ebx, 2		jnz			col_loop		/* skip to next row */		mov			ebx, tmp			lea			ebx, [ebx + 4*ecx]		add			edx, ecx		mov			tmp, ebx		pop			ebx		sub			ebx, 2		jnz			row_loop		emms				/* clear FPU state */		popad				/* pop all dword registers back from stack :) */	};	return SWC_OK;}


Hope it's useful,
Pat.

[edit]
You cannot convert alpha! Only color components can be transformed to YCrCb.
General conversion works by multiplying the rgb color vector with the conversion matrix as provided in the code sample.
Just invert the matrix to get the inverse transform. You can also google for these values as I used a standard PAL matrix. You might want to use other coefficients for NTSC or SECAM.
[/edit]
Opps, brain-fart. Ignore the code I posted. It transforms to YUV422
, which is not what you want.

At the heart of the color-space conversion is the matrix-multiply calculation. The multiplier coefficients depend on two variables: the range of the Y/CbCr output and the range of the RGB input. (Note: the offsets of 64 or 512 indicated in these equations are accomplished in a separate ‘bias adder’ function near the output)

The RGB to YcbCr matrix-multiply and offset equations are as follows:
Y = (Y Range/RGB Range)(0.299R + 0.587G + 0.114B) + 64
Cb = (Cb Range/RGB Range)(-0.1687R - 0.3313G + 0.5B) + 512
Cr = (Cr Range/RGB Range)(0.5R - 0.4187G - 0.0813B) + 512

You may look at the jpeglib source code, too. They have implemented this conversion (also using MMX, SSE and 3DNow! assembly).

Hope this helps,
Pat.
after a quick shifty at your posting history i'm guessing you are using OpenGL, if so you might want to look at using the Color Matrix to do your color space conversion.
The bonus is the color space conversion is basically free as it is ALWAYS performed and cant be disabled [smile].
just a word of warning about the OpenGL color matrix: I've had some different OpenGL drivers (different chip manufacturers, not just card models or driver versions) that completely ignored my color matrix stuff with no warnings (I was trying to do some grey-scale effects). I'm not sure how to test for a working implementation. And this was a while back...so maybe everything is fixed by now. Sorry if that didn't make any sense, I'm beat.
Quote:Original post by _the_phantom_
after a quick shifty at your posting history i'm guessing you are using OpenGL, if so you might want to look at using the Color Matrix to do your color space conversion.
The bonus is the color space conversion is basically free as it is ALWAYS performed and cant be disabled [smile].

Just because it's conceptually always enabled doesn't mean it's for free. A driver is free to internally skip the color matrix operation when the identity matrix is loaded, and just transfer the pixels as they are (assuming there are no other source to destination format conversions needed). So saying it's for free is a bit much.
In addition to that, a driver is free to not support the color matrix at all, since it is part of the optional ARB_imaging subset of GL. And it only applies to pixel transfer operation IIRC.

This topic is closed to new replies.

Advertisement