在 C# 中减少位图位大小

发布于 2024-08-30 06:38:42 字数 95 浏览 3 评论 0原文

我正在使用 C#,并将图像存储在对象 Bitmap 中。

现在我想将此图像转换为 8 位灰度图像,然后转换为 4 位灰度图像。

您有什么建议吗?

I am using C#, and having an image stored in the object Bitmap.

Now I would like to convert this image into 8bit greyscale, then into a 4-bit greyscale image.

Do you have any tips how this can be made?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

巾帼英雄 2024-09-06 06:39:11

在 .NET 位图格式中,不存在 8 位或 4 位灰度图像。支持的格式由 PixelFormat 枚举 枚举。但是,您可以通过创建索引图像(8bppIndexed 或 4bppIndexed)来创建 4 位或 8 位图像,其中调色板中的每个条目都是灰度值。

此代码采用位图并创建一个副本作为具有灰度值的 8bpp 索引图像:

    public static Bitmap BitmapToGrayscale(Bitmap source)
    {
        // Create target image.
        int width = source.Width;
        int height = source.Height;
        Bitmap target = new Bitmap(width,height,PixelFormat.Format8bppIndexed);
        // Set the palette to discrete shades of gray
        ColorPalette palette = target.Palette;            
        for(int i = 0 ; i < palette.Entries.Length ; i++)
        {                
            palette.Entries[i] = Color.FromArgb(0,i,i,i);
        }
        target.Palette = palette;

        // Lock bits so we have direct access to bitmap data
        BitmapData targetData = target.LockBits(new Rectangle(0, 0, width,height),
                                                ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
        BitmapData sourceData = source.LockBits(new Rectangle(0, 0, width,height),
                                                ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

        unsafe
        {
            for(int r = 0 ; r < height ; r++)
            {
                byte* pTarget = (byte*) (targetData.Scan0 + r*targetData.Stride);
                byte* pSource = (byte*) (sourceData.Scan0 + r*sourceData.Stride);
                for(int c = 0 ; c < width ; c++)
                {
                    byte colorIndex = (byte) (((*pSource)*0.3 + *(pSource + 1)*0.59 + *(pSource + 2)*0.11));
                    *pTarget = colorIndex;
                    pTarget++;
                    pSource += 3;
                }
            }
        }

        target.UnlockBits(targetData);
        source.UnlockBits(sourceData);
        return target;
    }

为了制作 4Bpp 图像,您需要使用 PixelFormat.Format4bppIndexed 创建目标,然后设置ColorPalette 为 16 种离散的灰色阴影。最后,在循环中,您应该将值 2 标准化为 0-15 之间,并将每 2 个像素值打包到一个字节中。

这是制作 4bpp 灰度图像的修改后的代码:

    public static Bitmap BitmapToGrayscale4bpp(Bitmap source)
    {
        // Create target image.
        int width = source.Width;
        int height = source.Height;
        Bitmap target = new Bitmap(width,height,PixelFormat.Format4bppIndexed);
        // Set the palette to discrete shades of gray
        ColorPalette palette = target.Palette;            
        for(int i = 0 ; i < palette.Entries.Length ; i++)
        {
            int cval = 17*i;
            palette.Entries[i] = Color.FromArgb(0,cval,cval,cval);
        }
        target.Palette = palette;

        // Lock bits so we have direct access to bitmap data
        BitmapData targetData = target.LockBits(new Rectangle(0, 0, width,height),
                                                ImageLockMode.ReadWrite, PixelFormat.Format4bppIndexed);
        BitmapData sourceData = source.LockBits(new Rectangle(0, 0, width,height),
                                                ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

        unsafe
        {
            for(int r = 0 ; r < height ; r++)
            {
                byte* pTarget = (byte*) (targetData.Scan0 + r*targetData.Stride);
                byte* pSource = (byte*) (sourceData.Scan0 + r*sourceData.Stride);
                byte prevValue = 0;
                for(int c = 0 ; c < width ; c++)
                {
                    byte colorIndex = (byte) ((((*pSource)*0.3 + *(pSource + 1)*0.59 + *(pSource + 2)*0.11)) / 16);
                    if (c % 2 == 0)
                        prevValue = colorIndex;
                    else
                        *(pTarget++) = (byte)(prevValue | colorIndex << 4);

                    pSource += 3;
                }
            }
        }

        target.UnlockBits(targetData);
        source.UnlockBits(sourceData);
        return target;
    }

In the .NET Bitmap formats, there are no such thing as a 8 or 4 bit grayscale image. The supported formats are enumerated by the PixelFormat enumeration. You can, however, create a 4 or 8 bit image by creating a indexed image (8bppIndexed or 4bppIndexed), where each entry in the palette is a greyscale value.

This code takes a Bitmap and creates a copy as a 8bpp indexed image with greyscale values:

    public static Bitmap BitmapToGrayscale(Bitmap source)
    {
        // Create target image.
        int width = source.Width;
        int height = source.Height;
        Bitmap target = new Bitmap(width,height,PixelFormat.Format8bppIndexed);
        // Set the palette to discrete shades of gray
        ColorPalette palette = target.Palette;            
        for(int i = 0 ; i < palette.Entries.Length ; i++)
        {                
            palette.Entries[i] = Color.FromArgb(0,i,i,i);
        }
        target.Palette = palette;

        // Lock bits so we have direct access to bitmap data
        BitmapData targetData = target.LockBits(new Rectangle(0, 0, width,height),
                                                ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
        BitmapData sourceData = source.LockBits(new Rectangle(0, 0, width,height),
                                                ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

        unsafe
        {
            for(int r = 0 ; r < height ; r++)
            {
                byte* pTarget = (byte*) (targetData.Scan0 + r*targetData.Stride);
                byte* pSource = (byte*) (sourceData.Scan0 + r*sourceData.Stride);
                for(int c = 0 ; c < width ; c++)
                {
                    byte colorIndex = (byte) (((*pSource)*0.3 + *(pSource + 1)*0.59 + *(pSource + 2)*0.11));
                    *pTarget = colorIndex;
                    pTarget++;
                    pSource += 3;
                }
            }
        }

        target.UnlockBits(targetData);
        source.UnlockBits(sourceData);
        return target;
    }

In order to make a 4Bpp image instead, you would need to create the target with PixelFormat.Format4bppIndexed, and then set the ColorPalette to 16 discrete shades of gray. Finally, in the loop you should normalize values 2 be between 0-15 and pack each 2 pixel values into a single byte.

This is the modified code to make a 4bpp greyscale image:

    public static Bitmap BitmapToGrayscale4bpp(Bitmap source)
    {
        // Create target image.
        int width = source.Width;
        int height = source.Height;
        Bitmap target = new Bitmap(width,height,PixelFormat.Format4bppIndexed);
        // Set the palette to discrete shades of gray
        ColorPalette palette = target.Palette;            
        for(int i = 0 ; i < palette.Entries.Length ; i++)
        {
            int cval = 17*i;
            palette.Entries[i] = Color.FromArgb(0,cval,cval,cval);
        }
        target.Palette = palette;

        // Lock bits so we have direct access to bitmap data
        BitmapData targetData = target.LockBits(new Rectangle(0, 0, width,height),
                                                ImageLockMode.ReadWrite, PixelFormat.Format4bppIndexed);
        BitmapData sourceData = source.LockBits(new Rectangle(0, 0, width,height),
                                                ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

        unsafe
        {
            for(int r = 0 ; r < height ; r++)
            {
                byte* pTarget = (byte*) (targetData.Scan0 + r*targetData.Stride);
                byte* pSource = (byte*) (sourceData.Scan0 + r*sourceData.Stride);
                byte prevValue = 0;
                for(int c = 0 ; c < width ; c++)
                {
                    byte colorIndex = (byte) ((((*pSource)*0.3 + *(pSource + 1)*0.59 + *(pSource + 2)*0.11)) / 16);
                    if (c % 2 == 0)
                        prevValue = colorIndex;
                    else
                        *(pTarget++) = (byte)(prevValue | colorIndex << 4);

                    pSource += 3;
                }
            }
        }

        target.UnlockBits(targetData);
        source.UnlockBits(sourceData);
        return target;
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文