Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
163 views
in Technique[技术] by (71.8m points)

c# - Iterate over image pixels and rotate Colors

I am wanting to be able to iterate over a bitmap image and increase the value by one, but currently the image comes back from the memory stream a poorer quality than before and the change is not smooth at all.

    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; 
        }
    }
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

First a word about GIF files and your original solution.

It is pretty simple to make it run real fast, but when, for fun, I did just that I had to laugh - now I know what you meant by the quality degraded..!!

Let me explain: There are a few options in GIF files, but here the important one is whether it is dithered or not.

I have already mentioned that they have only 256 colors; to look nice, which they can, at least from a distance, the normal GIF files use a trick: They dither blocks of pixels to show a mix of colors! This works pretty well, but it completely prohibits to use the palette for color rotation..

Of course one can switch off dithering but the result not only looks pretty coarse and pixelated; with a suitable palette it is possible to do the rotation but the result is mediocre at best. I have appended code to do that in the function palRotate.

Which leaves us with my original suggestion: Forget GIF and go for for ARGB-color rotation! This also allows you to work with the full range of Ks you have calculated.. But you need tight code to make it run fast.

Here is a complete testbed using LockBits to load data and do full color cycling with them. You need to add a PictureBox, a Button and a Timer to your form. Note that you need to keep the size of the data equal to the size of the PictureBox. I have included a 500x500 testdata file.

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();
}

Here are a few files with test data; the test file has a size of 500x500 pixels:

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

Update:

Here is code to do a plaette rotation on a non-dithered GIF file.

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;
}

For preparation these calls would extract the original palette colors into the colors list:

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]);

For this to look anything but totally crappy the pallete would have to have some sort of order; but even then the pixelated image will look pathetic..

It would be called in a similar way as the other rotation code from a Timer which advances the koffset variable.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...