ARGB to YCrCb
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
Some conversion code from my wavelet encoder:
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]
/************************************************ * 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.
, 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].
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
Popular Topics
Advertisement