6

I have a RGB image (RGB 4:4:4 colorspace, 24-bit per pixel), captured from camera. I use Gorgon 2D library (build base on SharpDX) to display this image as a texture so i have to convert it to ARGB. I use this code (not my code) to convert from RGB camera image to RGBA.

[StructLayout(LayoutKind.Sequential)]
public struct RGBA
{
            public byte r;
            public byte g;
            public byte b;
            public byte a;
}

[StructLayout(LayoutKind.Sequential)]
public struct RGB
{
            public byte r;
            public byte g;
            public byte b;
}

unsafe void internalCvt(long pixelCount, byte* rgbP, byte* rgbaP)
            {
                for (long i = 0, offsetRgb = 0; i < pixelCount; i += 4, offsetRgb += 12)
                {
                    uint c1 = *(uint*)(rgbP + offsetRgb);
                    uint c2 = *(uint*)(rgbP + offsetRgb + 3);
                    uint c3 = *(uint*)(rgbP + offsetRgb + 6);
                    uint c4 = *(uint*)(rgbP + offsetRgb + 9);
                    ((uint*)rgbaP)[i] = c1 | 0xff000000;
                    ((uint*)rgbaP)[i + 1] = c2 | 0xff000000;
                    ((uint*)rgbaP)[i + 2] = c3 | 0xff000000;
                    ((uint*)rgbaP)[i + 3] = c4 | 0xff000000;
                }
            }

public unsafe void RGB2RGBA(int pixelCount, byte[] rgbData, byte[] rgbaData)
            {
                if ((pixelCount & 3) != 0) throw new ArgumentException();
                fixed (byte* rgbP = &rgbData[0], rgbaP = &rgbaData[0])
                {
                    internalCvt(pixelCount, rgbP, rgbaP);
                }
            }

Then convert RGB to RGBA like this:

byte[] rgb = new byte[800*600*3]; //Stored data
byte[] rgba = new byte[800 * 600 * 4];
RGB2RGBA(800*600, rgb, rgba)

And I use rgba as data of Gorgon texture:

unsafe
{
     fixed(void* rgbaPtr = rgba)
     {
          var buff = new GorgonNativeBuffer<byte>(rgbaPtr, 800*600*4);                      
          GorgonImageBuffer imb = new GorgonImageBuffer(buff, 800, 600, BufferFormat.R8G8B8A8_UNorm);

          //Set Texture data  GorgonTexture2D                                
          Texture.SetData(imb, new SharpDX.Rectangle(0, 0, 800, 600), 0, 0, CopyMode.NoOverwrite);
         }
}

But the color of texture image is not like the color of camera image.

enter image description here

so I think I have to convert camera image to ARGB, (not RGBA) so that it can display with Gorgon texture but i dont know how to do it with the code above. Can you guys please point me some hints?

Thanks!

Below are the links to types of Gorgon Library i used in the code above

GorgonTexture2D GorgonNativeBuffer GorgonImageBuffer

Dominota
  • 141
  • 7
  • 10
    Just set the A-byte to 0xff ? – Lasse V. Karlsen Aug 23 '19 at 06:48
  • 1
    could you use system.drawing.color? there is method call Color.FromArgb() and Color.ToArgb(). – phonemyatt Aug 23 '19 at 06:52
  • 2
    Could you clarify the formats? For example this RGBA struct has R in the lowest byte, which is often called ABGR (so that a color in hex reads 0xAABBGGRR). And ARGB commonly means 0xAARRGGBB, which would have the R and B positions swapped but the A in the same position. There are some conflicting naming conventions unfortunately – harold Aug 23 '19 at 06:59
  • 1
    As RGB doesn't have an alpha value and therefore cannot be transparent we just set the A-byte to it's max value of 0xFF, meaning it is completely opaque – MindSwipe Aug 23 '19 at 07:02
  • 1
    `Color argb = Color.FromArgb(255, r, g, b)` should be as fast as the .NET coders got it. Before trying anything else I would go for that.. – TaW Aug 23 '19 at 07:04
  • @LasseVågsætherKarlsen Thanks, i will give a try – Dominota Aug 23 '19 at 07:05
  • @harold Frankly speaking, that is not my code so i dont understand what is inside. Thank you so much, i will try and feedback you soon. According to you, ((uint*)rgbaP)[i] = c1 | 0xff000000 will be ((uint*)rgbaP)[i] = 0xFF; – Dominota Aug 23 '19 at 07:29
  • @TaW i dont use Color.FromArgb because its speed – Dominota Aug 23 '19 at 07:29
  • @Dominota no that's not it, then you just get 0x000000FF for every colour (whatever that means, which is not clear yet) it would discard the input image. If you can clearly define the input and output byte orders though, I'm sure I could answer. It might be `(c1 << 8) | 0xFF` but it really depends on the formats. – harold Aug 23 '19 at 07:32
  • _I dont use Color.FromArgb because its speed_ Which you have measured? How? Or can you tell from the sources? For speed you can always use Lockbits and move the bytes directly. – TaW Aug 23 '19 at 09:08
  • @harold the input image format is RGB 4:4:4 colorspace (24-bit per pixel), captured from camera – Dominota Aug 23 '19 at 14:56
  • internalCvt() is wrong. It needs to read *three* bytes and write *one* uint. – Hans Passant Aug 23 '19 at 15:38
  • @Dominota `R8G8B8A8_UNorm` has the same format as `internalCvt` produces, at least same-enough (A in the top byte of the uint so it correctly sets the A channel to FF), that just leaves a question about whether the format from the camera has the byte order R G B or B G R. Try if the code already works, if the colours look off then R and B need to be swapped which is a little more tricky. – harold Aug 23 '19 at 18:40
  • @HansPassant No, i tried to save rgba to image on disk, it is ok. The problem is my rgba and Gorgon texture. I am stuck here. – Dominota Aug 24 '19 at 00:22
  • @EJoshuaS, Please tell me what is not clear in my question? – Dominota Aug 24 '19 at 08:49

1 Answers1

4

The byte order of the target is, in memory, R G B A. The byte order of the source is, in memory, B G R. So in addition to expanding every 3 bytes to 4 bytes and putting FF in the new A channel, the R and B channel need to swap position. For example,

unsafe void internalCvt(long pixelCount, byte* rgbP, uint* rgbaP)
{
    for (long i = 0, offsetRgb = 0; i < pixelCount; i += 4, offsetRgb += 12)
    {
        uint c1 = *(uint*)(rgbP + offsetRgb);
        uint c2 = *(uint*)(rgbP + offsetRgb + 3);
        uint c3 = *(uint*)(rgbP + offsetRgb + 6);
        uint c4 = *(uint*)(rgbP + offsetRgb + 9);
        // swap R and B
        c1 = (c1 << 16) | (c1 & 0xFF00) | ((c1 >> 16) & 0xFF);
        c2 = (c2 << 16) | (c2 & 0xFF00) | ((c2 >> 16) & 0xFF);
        c3 = (c3 << 16) | (c3 & 0xFF00) | ((c3 >> 16) & 0xFF);
        c4 = (c4 << 16) | (c4 & 0xFF00) | ((c4 >> 16) & 0xFF);
        // set alpha to FF
        rgbaP[i] = c1 | 0xff000000;
        rgbaP[i + 1] = c2 | 0xff000000;
        rgbaP[i + 2] = c3 | 0xff000000;
        rgbaP[i + 3] = c4 | 0xff000000;
    }
}

Or a simpler version that processes one pixel per iteration and doesn't use unsafe,

void internalCvt(long pixelCount, byte[] bgr, byte[] rgba)
{
    long byteCount = pixelCount * 4;
    for (long i = 0, offsetBgr = 0; i < byteCount; i += 4, offsetRgb += 3)
    {
        // R
        rgba[i] = bgr[offsetBgr + 2];
        // G
        rgba[i + 1] = bgr[offsetBgr + 1];
        // B
        rgba[i + 2] = bgr[offsetBgr];
        // A
        rgba[i + 3] = 0xFF;
    }
}
harold
  • 61,398
  • 6
  • 86
  • 164
  • Thank you so much, but the convert function is: public static unsafe void RGB2RGBA_FastConvert4v2(int pixelCount, byte[] rgbData, byte[] rgbaData)) so how can it call your function in RGB2RGBA_FastConvert4v2 function with rgbaData is a byte[] – Dominota Aug 24 '19 at 00:40
  • @Dominota you can just cast the pointer like it was in the original code, I moved that out so it was a little less redundant in that aspect, eg `internalCvt(pixelCount, rgbP, (uint*)rgbaP)` – harold Aug 24 '19 at 00:41
  • OMG! It works like charm. You save my life :)) @harold. Thank you so much! – Dominota Aug 24 '19 at 00:47
  • This is the first time i ask a question on StackOverflow and i got wonderful helps from everyone. Thank you guys, thank you StackOverflow!!!!! – Dominota Aug 24 '19 at 01:16
  • In your invervnalCvt, when i want to convert RGBA to ARGB, do i dont need swap R and B, only set alpha to FF? – Dominota Aug 28 '19 at 01:38
  • @Dominota if those are both in DXGI naming convention, then it would mean moving the A down from the top byte to the low byte, while shifting the RGB triple up. So `(c1 >> 24) | (c1 << 8)`. Or if you want to simultaneously set A to FF then `(c1 << 8) | 0xFF`. – harold Aug 28 '19 at 01:45
  • i mean, i convert RGB image camera to RGBA then convert RGBA to ARGB – Dominota Aug 28 '19 at 01:51
  • you mean?: ((uint*)argbP)[i] = c4; //a ((uint*)argbP)[i + 1] = c1; //r ((uint*)argbP)[i + 2] = c2; //g ((uint*)argbP)[i + 3] = c3; //b – Dominota Aug 28 '19 at 02:14
  • @Dominota I don't really understand what kind of output you want, but the code you just commented would rearrange pixels and it is not true that `c4` corresponds to alpha etc, the loop (like the original) processes 4 pixels per iteration. E: how about I write up something simpler so you can easily mess around with it – harold Aug 28 '19 at 02:16
  • Thank you @Harold, you got the answer – Dominota Aug 29 '19 at 08:38