【问题标题】:Rotate image with indexed pixel format?使用索引像素格式旋转图像?
【发布时间】:2012-07-31 23:45:22
【问题描述】:

我试图弄清楚如何将我的位图图像旋转任意度数,类似于solution posted here。但是,当我尝试使用此解决方案(将位图转换为图形对象)以及我在 Google 上搜索过的其他解决方案时,我不断收到异常:

A Graphics object cannot be created from an image that has an indexed pixel format.

我需要索引像素,以便我可以访问每个像素并在我的程序中获取颜色信息,但我还需要能够以 90 度以外的角度增量动态旋转。

我想知道 A) 是否有对索引像素位图友好的解决方案,或者 B) 是否有办法临时更改位图以允许将其转换为图形对象,然后将其更改回应用了旋转的索引像素位图?

非常感谢!

编辑:我使用的位图的 PixelFormat 是“1bppIndexed”。

【问题讨论】:

  • 您不需要索引像素格式就可以直接检索像素数据 - indexed format 指的是像素值被索引到图像调色板中。也许您应该尝试使用非索引格式...
  • 你到底为什么还在使用 8bpp 图像?现在已经不是 1993 年了。
  • 我认为“索引格式”意味着我可以使用 GetPixel() 和 SetPixel()。只要我可以使用这些功能,我就不关心格式的细节。也许我严重误解了“索引格式”的含义。 :)
  • 找到解决方案;在下面发布了一个链接作为答案。

标签: c# .net image image-processing bitmap


【解决方案1】:

我想您在这里使用的是 Windows 窗体和 GDI+?我已经为此做了很多工作,并且可以非常自信地告诉您您想要使用 24 位或 32 位图像(我不记得哪个对不起)。性能大大提高了大约 10 到 100 倍。在我的应用程序中,用户在文本框中指定旋转与能够用鼠标抓取图像并动态平滑旋转、动态缩放等有所不同。

假设您遵循的建议只是留下了能够访问每个像素的问题。为什么您认为无法获取 24 位或 32 位图像的每个像素?

编辑:在我的应用程序中,我只对所有内容使用了 32 位位图。如果我得到了另一种格式的位图(例如从文件中打开),我会立即对其进行转换。实际上,即使我得到 32 位格式,我也会立即克隆它,然后处理原始位图,因为我发现它在文件上保留了一些锁。内置克隆产生了奇怪的结果,所以我最终在下面编写了这段代码。请注意,这是我大概 8 年前写的东西,所以没有使用任何最新功能:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace ImageCloneTest
{
    static class Program
    {
        [DllImport("kernel32", EntryPoint = "RtlMoveMemory")]
        public unsafe static extern void CopyMemory(byte* Destination, byte* Source, int Length);

        [DllImport("kernel32", EntryPoint = "RtlMoveMemory")]
        public static extern void CopyMemory(IntPtr Destination, IntPtr Source, int Length);


        public static Bitmap Clone(Bitmap SourceBitmap, PixelFormat Format)
        {
            // copy image if pixel format is the same
            if (SourceBitmap.PixelFormat == Format) return Clone(SourceBitmap);

            int width = SourceBitmap.Width;
            int height = SourceBitmap.Height;

            // create new image with desired pixel format
            Bitmap bitmap = new Bitmap(width, height, Format);

            // draw source image on the new one using Graphics
            Graphics g = Graphics.FromImage(bitmap);
            //fill background in case orig bitmap contains transparent regions.
            g.FillRectangle(Brushes.White, 0, 0, bitmap.Width, bitmap.Height);
            g.DrawImage(SourceBitmap, 0, 0, width, height);
            g.Dispose();

            return bitmap;
        }

        // and with unspecified PixelFormat it works strange too
        public static Bitmap Clone(Bitmap SourceBitmap)
        {
            // get source image size
            int width = SourceBitmap.Width;
            int height = SourceBitmap.Height;
            Rectangle rect = new Rectangle(0, 0, width, height);

            // lock source bitmap data
            BitmapData srcData = SourceBitmap.LockBits(rect, ImageLockMode.ReadOnly, SourceBitmap.PixelFormat);

            // create new image
            Bitmap dstBitmap = new Bitmap(width, height, SourceBitmap.PixelFormat);

            // lock destination bitmap data
            BitmapData dstData = dstBitmap.LockBits(rect, ImageLockMode.WriteOnly, dstBitmap.PixelFormat);

            CopyMemory(dstData.Scan0, srcData.Scan0, height * srcData.Stride);

            // unlock both images
            dstBitmap.UnlockBits(dstData);
            SourceBitmap.UnlockBits(srcData);

            // copy pallete
            if (BitmapHasPalette(SourceBitmap) && BitmapHasPalette(dstBitmap))
            {
                ColorPalette srcPalette = SourceBitmap.Palette;
                ColorPalette dstPalette = dstBitmap.Palette;
                int n = srcPalette.Entries.Length;
                for (int i = 0; i < n; i++)
                {
                    dstPalette.Entries[i] = srcPalette.Entries[i];
                }
                dstBitmap.Palette = dstPalette;
            }
            return dstBitmap;
        }

        public static bool BitmapHasPalette(Bitmap SourceBitmap)
        {
            if (SourceBitmap == null) return false;
            switch (SourceBitmap.PixelFormat)
            {
                case PixelFormat.Format1bppIndexed:
                case PixelFormat.Format4bppIndexed:
                case PixelFormat.Format8bppIndexed:
                case PixelFormat.Indexed:
                    return true;
            }
            return false;
        }
    }
}

【讨论】:

  • 我使用的是 Windows 窗体,虽然我没有显示位图。我只是通过旋转来操作它,然后如果它是黑色的,则使用 GetPixel() 在文本框中显示它的像素,以便稍后进行一些 OCR 工作。
【解决方案2】:

我在这里使用了 Hans 的解决方案:https://stackoverflow.com/a/2016509/1464630

看来我仍然可以使用相同的程序逻辑,但它不再采用索引像素格式,因此 Graphics 类不会抱怨。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 2013-12-30
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    • 2022-01-21
    • 2014-04-25
    相关资源
    最近更新 更多