【问题标题】:Iterate over image pixels and rotate Colors迭代图像像素并旋转颜色
【发布时间】:2014-11-04 18:46:34
【问题描述】:

我希望能够遍历位图图像并将值增加一,但目前从内存流中返回的图像质量比以前差,并且变化根本不平滑。

    private static Bitmap CyclePalette(Bitmap original)
    {
        using (var bitMapStream = new MemoryStream())
        {
            original.Save(bitMapStream, format: ImageFormat.Gif);
            var newBitmap = new Bitmap(bitMapStream);

            var newPalette = newBitmap.Palette;

            for (var i = 0; i < newPalette.Entries.Length - 5; i++)
            {

                var oldColor = newPalette.Entries[i];
                newPalette.Entries[i] = newPalette.Entries[i + 1];
                newPalette.Entries[i + 1] = oldColor;

            }
            newBitmap.Palette = newPalette;
            return newBitmap; 
        }
    }

【问题讨论】:

  • 如何使用 Bitmap.Clone() 来做我想做的事?
  • 另外,GIF generally only supports 256 colors。如果您的位图有更多,那可能解释了您的问题。像MemoryBmp 或者Png 这样的东西会更好。
  • 在这种情况下,构建一个调色板,以平滑序列更改 rgb 值,为特殊区域保留一些(迭代是“无限”的,正如您所做的那样)。如果不使用循环,而是使用memcpy() 来移动调色板的大部分,调色板旋转会更快。但速度缓慢可能是由于将整个图像提供给渲染引擎,而不仅仅是调色板。
  • 我已经添加了一个解释为什么你的方法不能很好地工作,以及一个对我来说效果很好的解决方案..
  • 我希望你喜欢这个解决方案;您可能仍然对更新感兴趣。我错了 GDI 在未抖动的 GIF 文件上崩溃;我的文件扩展名错误。所以我添加了代码来快速旋转 GIF 调色板。但与 PaintKs 中的解决方案相比,它总是看起来很差。

标签: c# winforms


【解决方案1】:

首先谈谈 GIF 文件和您的原始解决方案。

让它运行得非常快很简单,但是为了好玩,我只是笑了 - 现在我知道你说的质量下降是什么意思了..!!

让我解释一下:GIF 文件中有几个选项,但这里重要的是它是否抖动。

我已经提到它们只有 256 种颜色;为了看起来不错,至少从远处看,普通的 GIF 文件使用了一个技巧:它们抖动像素块来显示混合颜色!这很好用,但它完全禁止使用调色板进行颜色旋转..

当然可以关闭抖动,但结果不仅看起来很粗糙而且像素化;使用合适的调色板可以进行旋转,但结果充其量是平庸的。我已经在函数palRotate 中附加了执行此操作的代码。

这给我们留下了我最初的建议:忘记 GIF 并使用 ARGB 颜色旋转!这也允许您使用已计算的所有 Ks 范围。但您需要紧凑的代码以使其快速运行。

这是一个完整的测试平台,使用 LockBits 加载数据并使用它们进行全彩色循环。 您需要在表单中添加 PictureBoxButtonTimer。 请注意,您需要保持数据的大小等于PictureBox 的大小。 我已经包含了一个 500x500 的测试数据文件。

using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.IO;
//..

// the data array
int[,] Ks;
// the offset for cycling
int koffset = 0;
// a list of colors
List<Color> colors = new List<Color>();

public void paintKs()
{
    if (Ks == null) return;

    Size s1 = pb_image.ClientSize;
    pb_image.Image = new Bitmap(s1.Width, s1.Height);
    Bitmap bmp = new Bitmap(pb_image.Image);

    PixelFormat fmt1 = bmp.PixelFormat;
    byte bpp1 = 4;

    Rectangle rect = new Rectangle(Point.Empty, s1);
    BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, fmt1);

    int size1 = bmpData.Stride * bmpData.Height;
    byte[] data = new byte[size1];
    System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, data, 0, size1);

    for (int y = 0; y < s1.Height; y++)
    {
        for (int x = 0; x < s1.Width; x++)
        {
            int index = y * bmpData.Stride + x * bpp1;
            Color c = colors[(Ks[x, y] + koffset) % (colors.Count)];
            if (Ks[x, y] == 0) c = Color.Black;
            data[index + 0] = c.B;
            data[index + 1] = c.G;
            data[index + 2] = c.R;
            data[index + 3] = 255;
        }
    }

    System.Runtime.InteropServices.Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
    bmp.UnlockBits(bmpData);

    pb_image.Image = bmp;
}

void saveKs(string dataFile)
{
   using (BinaryWriter writer = new BinaryWriter(File.Open(dataFile, FileMode.Create)))
   {
        for (int y = 0; y < Ks.GetLength(0); y++)
            for (int x = 0; x < Ks.GetLength(1); x++)
                writer.Write((Int16)Ks[x, y]);
   }
}

void loadKs(string dataFile)
{
   int w = pb_image.ClientSize.Width;
   if (Ks == null) Ks = new int[w, w];

   using (BinaryReader reader = new BinaryReader(File.Open(dataFile, FileMode.Open)))
   {
        for (int y = 0; y < Ks.GetLength(0); y++)
            for (int x = 0; x < Ks.GetLength(1); x++)
                 Ks[x, y] = reader.ReadInt16();
   }

}

private void Test_Click(object sender, EventArgs e)
{
    loadKs("fractalData021.dat");
    for (int i = 0; i < 256; i++)
    {
        // a very simple and rather awful palette!
        for (int i = 0; i < 256; i++)  
             colors.Add(Color.FromArgb(255, i, i, 255 - i));
        for (int i = 0; i < 100; i++) 
             colors.Add(Color.FromArgb(255, i + 100, 255 -i, 155 - i));
        for (int i = 0; i < 100; i++) 
             colors.Add(Color.FromArgb(255, i + i+ 50, 255 - i - i, 155 - i/2));
    }
    paintKs();
    timer1.Intervall = 33;  // 30 fps
    timer1.Start();
}

private void timer1_Tick(object sender, EventArgs e)
{
    koffset++;
    if (koffset >= colors.Count) koffset = 0;;
    paintKs();
}

这里有一些带有测试数据的文件;测试文件的大小为 500x500 像素:

http://www.file-upload.net/download-9796723/fractalData021.dat.html

http://www.file-upload.net/download-9796722/fractalData021.jpg.html

http://www.file-upload.net/download-9796721/fractalData021.txt.html

更新:

这是在非抖动 GIF 文件上执行 plaette 旋转的代码。

void palRotate()
{
    Bitmap bmp = (Bitmap)pb_image.Image;
    var pal = bmp.Palette;
    for (int i = 0; i < 256; i++)  pal.Entries[(i + koffset) % 256] = colors[i];
    bmp.Palette = pal;
    pb_image.Image = bmp;
}

为了准备,这些调用会将原始调色板颜色提取到颜色列表中:

pb_image.Image = new Bitmap("d:\\fff.gif");
Bitmap bmp = (Bitmap)pb_image.Image;
var pal = bmp.Palette;
for (int i = 0; i < 256; i++) colors.Add(pal.Entries[i]);

为了让它看起来一点也不糟糕,托盘必须有某种顺序;但即便如此,像素化图像看起来也很可悲..

它的调用方式与 Timer 中的其他轮换代码类似,后者推进 koffset 变量。

【讨论】:

    【解决方案2】:

    这将创建一个可以平滑旋转颜色的调色板。碰巧 3 * 85 = 255 为 Mset 像素留下 1 个调色板条目。我没有对其进行编码以适应您的平台(最好将其编码为结构数组),但您可以看到我一次淡化 2 个原色。这样可以避免颜色浑浊。

    #include <stdio.h>
    #include <memory.h>
    
    #define MAXITER 1000   // iteration depth
    
    unsigned char pal [256][3];
    
    int Mandelplot (int x, int y) {
        // return (palette) colour for pixel
        int c, i=iterate (x, y);      // your iteration function
        if (i >= MAXITER)
            c = 255;
         else
            c = i % 255;
        return c;
    }
    
    void rotate_palette (void) {
        // leaves index 255 unchanged for Mset pixels
        unsigned char r=pal[0][0], g=pal[0][1], b=pal[0][2];
        memcpy (&pal[0][0], &pal[1][0], 254 * 3 * sizeof(unsigned char));
        pal[254][0] = r;
        pal[254][1] = g;
        pal[254][2] = b;
    }
    
    int main (void) {
        unsigned char r=0, g=0, b=255;
        int i;
    
        for (i=0; i<85; i++) {
            pal [i][0]= r;
            pal [i][1]= g;
            pal [i][2]= b;
            b -= 3;
            g += 3;
        }
    
        for (i=85; i<170; i++) {
            pal [i][0]= r;
            pal [i][1]= g;
            pal [i][2]= b;
            g -= 3;
            r += 3;
        }
    
        for (i=170; i<255; i++) {
            pal [i][0]= r;
            pal [i][1]= g;
            pal [i][2]= b;
            r -= 3;
            b += 3;
        }
    
        pal [255][0] = 0;  // black on the Mset
        pal [255][1] = 0;
        pal [255][2] = 0;
    
        for (i=0; i<256; i++)
           printf ("%02X %02X %02X, ", pal[i][0], pal[i][1], pal[i][2]);
    
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2012-02-08
      • 2011-06-07
      • 2013-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-21
      • 1970-01-01
      • 2020-05-08
      相关资源
      最近更新 更多