RGB formats in .NET Color
Asked Answered
M

1

2

I have been checking the documentation on Color (I am writing a C# program) Setting a color to Color

If you see there, it sets a color based on 8-bit values of RGB. In other words, this is I suppose a RGB-888 format...I guess.

I am using in my camera a RGB-565 format (5 bits for R,B and 6 for G) Is there a way to set Color to a color based on this format and not 888? Or do I have to do this manually myself?

Margo answered 28/4, 2017 at 8:41 Comment(8)
Any color that is representable in a 5-6-5 format is representable in an 8-8-8 format. (.NET actually uses an 8-8-8-8 format; the last 8 bits are for the alpha-channel. You can ignore that, of course, if you don't need it. An opaque color has its alpha-channel set to 255.)Guayule
Hmm, about any camera's firmware supports converting the camera's images into a more suitable format. Do be careful that you did not make this intentionally cumbersome. Even a method like Bitmap.LockBits() allows you to make the conversion by specifying the pixel format. It is not for free. Converting 565 to 888 requires bit-shifts with the << operator, that is not for free either.Misdeem
@CodyGray Yes, that is what I am asking. Does .NET covers this format 565 or do I have to convert it myself?Margo
@HansPassant Oh, I don't understand LockBits yet, but I took a glance that it has something to do with 565 in the PixelFormat. So .NET has something to do with 565 after all... The thing is, should I use this seemingly complicated way, or just do the shifts myself... it seems simpler...Margo
You have not explained why you preferred to operate the camera in 16-bit mode. Please do not force us to guess which is simpler, using a 24-bit pixel format is a lot simpler.Misdeem
I don't understand why you would need to work in 5-6-5 format. The camera is sending pictures to you, not the other way around, so even if it sends you pictures in 5-6-5 format, you should convert them into 8-8-8 format for editing purposes. Not only is it simpler, it's faster to work with them this way (minus the one-time cost of conversion, of course). It is not simpler to do the shifts yourself. The code is hard to get right, harder to get optimal, and nowhere near as easy as a simple method call specifying the pixel format of the source.Guayule
Yes, the .NET Framework understands how to do the necessary conversions. That isn't the same as supporting operations directly on 5-6-5 colors. But again, it's unclear why you would ever want to do this, since 8-8-8 is a perfect superset of 5-6-5 and is directly supported for manipulations.Guayule
@CodyGray Well the camera I am using only supports RGB565, 555 and 444. Not 888. The TFT LCD I use uses 565. Now I am trying to send this to the PC. Which uses 888. Can you give me a hint of how to easily do this conversions?Margo
T
3

I have recently written code to do exactly that, based on the generic colour format converter of the ScummVM project.

The answer is on a different question here on StackOverflow, and although the question isn't really a duplicate, I think, the answer is, so I'll just link to it:

Working with 10 bits depth digital image

Mind you, I'm not sure exactly how the multiplier for each factor works on the 5-6-5 format. Is the 6-bit component simply more accurate? In that case, the automated conversion system will do the job.

Anyway, with the code linked above, this example should suit your needs perfectly. Here it is used to convert a custom 5-5-5-1 RGBA format:

//bytes 84 21 ==> 0x8421 (BE) ==bin==> 1000 0100 0010 0001 ==split==> 10000 10000 10000 1 ==dec==> 16 16 16 1 (RGBA) ==adjust==> 128 128 128 255
// values in constructor are: bytes per pixel, amount of bits and amount to shift for getting R, G, B and A components, and data endianness.
private static PixelFormatter SixteenBppFormatter = new PixelFormatter(2, 5, 11, 5, 6, 5, 1, 1, 0, false);

protected static Byte[] Convert16bTo32b(Byte[] imageData, Int32 startOffset, Int32 width, Int32 height, ref Int32 stride)
{
    Int32 newImageStride = width * 4; ;
    Byte[] newImageData = new Byte[height * newImageStride];
    for (Int32 y = 0; y < height; y++)
    {
        for (Int32 x = 0; x < width; x++)
        {
            Int32 sourceOffset = y * stride + x * 2;
            Int32 targetOffset = y * newImageStride + x * 4;
            Color c = SixteenBppFormatter.GetColor(imageData, startOffset + sourceOffset);
            PixelFormatter.Format32BitArgb.WriteColor(newImageData, targetOffset, c);
        }
    }
    stride = newImageStride;
    return newImageData;
}

All you need to do is define your own PixelFormatter with the correct bits distribution for the 5-6-5 format.

You will indeed need to look into Bitmap.LockBits() to get the original 16-bit data out of the image, and to write that data into a new 32-bit ARGB image. My BuildImage function mentioned in this answer should show how to handle the writing. The read method is actually a lot simpler:

/// <summary>
/// Gets the raw bytes from an image.
/// </summary>
/// <param name="sourceImage">The image to get the bytes from.</param>
/// <param name="stride">Stride of the retrieved image data.</param>
/// <returns>The raw bytes of the image</returns>
public static Byte[] GetImageData(Bitmap sourceImage, out Int32 stride)
{
    BitmapData sourceData = sourceImage.LockBits(new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), ImageLockMode.ReadOnly, sourceImage.PixelFormat);
    stride = sourceData.Stride;
    Byte[] data = new Byte[stride * sourceImage.Height];
    Marshal.Copy(sourceData.Scan0, data, 0, data.Length);
    sourceImage.UnlockBits(sourceData);
    return data;
}

Do note, in all situations where you edit raw image bytes, the difference between "stride" and "width". In a lot of formats, one line of pixels in an image is padded to the next multiple of four bytes, so you can't just read and process it as array assuming it's all image data; those padding bytes will mess that up really quickly. As shown in my example code to convert my 16bpp format to ARGB, you really need to do that line by line, and per line make sure you only use the data that is still within the (width * bytes per pixel) range.

I've noticed that for all functions that may change the stride, it's advised to give it as ref parameter.

Torosian answered 30/4, 2017 at 12:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.