C# 二次图像创建

C# two pass image creation

所以,我正在构建一个控制台应用程序来生成 2D perlin(ish) 噪声,该噪声在多个图像文件之间进行分割。我使用的算法要求我分两次进行生成,一次创建噪声种子,然后生成噪声。我将生成的噪音保存到生成种子的同一个文件中,以减少磁盘 - space,并最终使其干净。不过,我 运行 遇到了一个问题,在第二遍期间,我实际上无法访问该文件,因为它已被另一个进程使用。这是完整的代码,看看我是否遗漏了什么:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace MapGenerator
{
class Program
{
    public static Random rng = new Random();

    public static void Seed(int px, int py)
    {
        Bitmap seed = new Bitmap(256, 256, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
        for (int x = 0; x < seed.Width; x++)
        {
            for (int y = 0; y < seed.Height; y++)
            {
                var val = rng.Next(0, 255);
                seed.SetPixel(x, y, Color.FromArgb(255, val, val, val));
            }
        }

        if (File.Exists("chunk," + (px - 1) + "," + (py) + ".jpeg"))
        {
            Bitmap source = new Bitmap("chunk," + (px - 1) + "," + (py) + ".jpeg");
            for (int y = 0; y < 256; y++)
            {
                seed.SetPixel(0, y, Color.FromArgb(255, source.GetPixel(255, y).R, source.GetPixel(255, y).G, source.GetPixel(255, y).B));
            }
        }
        if (File.Exists("chunk," + (px + 1) + "," + (py) + ".jpeg"))
        {
            Bitmap source = new Bitmap("chunk," + (px + 1) + "," + (py) + ".jpeg");
            for (int y = 0; y < 256; y++)
            {
                seed.SetPixel(255, y, Color.FromArgb(255, source.GetPixel(0, y).R, source.GetPixel(0, y).G, source.GetPixel(0, y).B));
            }
        }
        if (File.Exists("chunk," + (px) + "," + (py - 1) + ".jpeg"))
        {
            Bitmap source = new Bitmap("chunk," + (px) + "," + (py - 1) + ".jpeg");
            for (int x = 0; x < 256; x++)
            {
                seed.SetPixel(x, 0, Color.FromArgb(255, source.GetPixel(x, 255).R, source.GetPixel(x, 255).G, source.GetPixel(x, 255).B));
            }
        }
        if (File.Exists("chunk," + (px) + "," + (py + 1) + ".jpeg"))
        {
            Bitmap source = new Bitmap("chunk," + (px) + "," + (py + 1) + ".jpeg");
            for (int x = 0; x < 256; x++)
            {
                seed.SetPixel(x, 255, Color.FromArgb(255, source.GetPixel(x, 0).R, source.GetPixel(x, 0).G, source.GetPixel(x, 0).B));
            }
        }
        seed.Save("chunk," + px + "," + py + ".jpeg", System.Drawing.Imaging.ImageFormat.Jpeg);
        seed.Dispose();
    }

    public static void Perlin(int px, int py)
    {
        Bitmap seed = new Bitmap("chunk," + px + "," + py + ".jpeg");

        Bitmap output = new Bitmap(256, 256, System.Drawing.Imaging.PixelFormat.Format24bppRgb);


        for (int y = 0; y < 256; y++)
        {
            for (int x = 0; x < 256; x++)
            {
                double noise = 0.0;
                double scale = 1.0;
                double acc = 0.0;

                for (int o = 0; o < 8; o++)
                {
                    int pitch = 256 >> o;
                    int sampleX1 = (x / pitch) * pitch;
                    int sampleY1 = (y / pitch) * pitch;


                    int sampleX2 = (sampleX1 + pitch);
                    int sampleY2 = (sampleY1 + pitch);

                    sampleX2 = (sampleX2 == 256) ? 255 : sampleX2;
                    sampleY2 = (sampleY2 == 256) ? 255 : sampleY2;

                    double Xblend = (double)(x - sampleX1) / (double)pitch;
                    double Yblend = (double)(y - sampleY1) / (double)pitch;

                    // interpolate between the two points
                    double Tsample = ((1 - Xblend) * ((double)seed.GetPixel(sampleX1, sampleY1).R / 255.0)) + (Xblend * ((double)seed.GetPixel(sampleX2, sampleY1).R / 255.0));
                    double Bsample = ((1 - Xblend) * ((double)seed.GetPixel(sampleX1, sampleY2).R / 255.0)) + (Xblend * ((double)seed.GetPixel(sampleX2, sampleY2).R / 255.0));

                    noise += (((1 - Yblend) * Tsample) + (Yblend * Bsample)) * scale;
                    acc += scale;
                    scale = scale * 0.6;
                }

                noise = noise / acc;

                noise = noise * 255.0;

                output.SetPixel(x, y, Color.FromArgb(255, (int)(noise), (int)(noise), (int)(noise)));
            }
        }
        seed.Dispose();
        File.Delete("chunk," + px + "," + py + ".jpeg");
        output.Save("chunk," + px + "," + py + ".jpeg", System.Drawing.Imaging.ImageFormat.Jpeg);
        output.Dispose();
    }

    static void Main(string[] args)
    {
        int depth = 1;

        for(int x = -depth; x <= depth; x++)
        {
            for(int y = -depth; y <= depth; y++)
            {
                Seed(x, y);
            }
        }

        for (int x = -depth; x <= depth; x++)
        {
            for (int y = -depth; y <= depth; y++)
            {
                Perlin(x, y);
            }
        }
    }
}
}

如您所见,我尝试了很多处理变量的方法,但都无济于事。任何想法我做错了什么?奇怪的是,当它只有一个功能时运行,我可以随意覆盖基本图像文件,但现在我把它分成两个不同的功能,这一切都是下坡路,它不会让我操纵文件。

您需要像下面这样处理您的位图

另外我会使用 Lockbits 而不是 GetPixelSetPixel 并且你会得到更好的性能因素

public static void Seed(int px, int py)
{
    using (var seed = new Bitmap(256, 256, PixelFormat.Format24bppRgb))
    {
        for (var x = 0; x < seed.Width; x++)
            for (var y = 0; y < seed.Height; y++)
            {
                var val = rng.Next(0, 255);
                seed.SetPixel(x, y, Color.FromArgb(255, val, val, val));
            }       

        if (File.Exists("chunk," + (px - 1) + "," + py + ".jpeg"))
            using (var source = new Bitmap("chunk," + (px - 1) + "," + py + ".jpeg"))
                for (var y = 0; y < 256; y++)
                    seed.SetPixel(0, y, Color.FromArgb(255, source.GetPixel(255, y).R, source.GetPixel(255, y).G, source.GetPixel(255, y).B));           

        if (File.Exists("chunk," + (px + 1) + "," + py + ".jpeg"))
            using (var source = new Bitmap("chunk," + (px + 1) + "," + py + ".jpeg"))
                for (var y = 0; y < 256; y++)
                    seed.SetPixel(255, y, Color.FromArgb(255, source.GetPixel(0, y).R, source.GetPixel(0, y).G, source.GetPixel(0, y).B));      

        if (File.Exists("chunk," + px + "," + (py - 1) + ".jpeg"))
            using (var source = new Bitmap("chunk," + px + "," + (py - 1) + ".jpeg"))
                for (var x = 0; x < 256; x++)
                    seed.SetPixel(x, 0, Color.FromArgb(255, source.GetPixel(x, 255).R, source.GetPixel(x, 255).G, source.GetPixel(x, 255).B));

        if (File.Exists("chunk," + px + "," + (py + 1) + ".jpeg"))
            using (var source = new Bitmap("chunk," + px + "," + (py + 1) + ".jpeg"))
                for (var x = 0; x < 256; x++)
                    seed.SetPixel(x, 255, Color.FromArgb(255, source.GetPixel(x, 0).R, source.GetPixel(x, 0).G, source.GetPixel(x, 0).B));

        seed.Save("chunk," + px + "," + py + ".jpeg", ImageFormat.Jpeg);
    }
}

示例如何使用 LockBits 和使用 unsafe 关键字的指针。基本上你是通过固定数组中的扫描线访问你的图片,使用指针直接访问内存中的 32 位 ARBG 值

using (var seed = new Bitmap(256, 256, PixelFormat.Format24bppRgb))
{
   // lock the array for direct access
   var bitmapData = seed.LockBits(new Rectangle(0,0,seed.Width,seed.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppPArgb);
   // get the pointer
   var scan0Ptr = (int*)bitmapData.Scan0;
   // get the stride
   var stride = bitmapData.Stride / 4;

   for (var x = 0; x < seed.Width; x++)
      for (var y = 0; y < seed.Height; y++)
      {
         var val = rng.Next(0, 255);
         *(scan0Ptr + x + y * stride) = Color.FromArgb(255, val, val, val).ToArgb();
      }

   seed.UnlockBits(bitmapData);
   ...
if (File.Exists("chunk," + (px - 1) + "," + (py) + ".jpeg"))
    {
        Bitmap source = new Bitmap("chunk," + (px - 1) + "," + (py) + ".jpeg");
        for (int y = 0; y < 256; y++)
        {
            seed.SetPixel(0, y, Color.FromArgb(255, source.GetPixel(255, y).R, source.GetPixel(255, y).G, source.GetPixel(255, y).B));
        }
        source.Dispose();
    }
    if (File.Exists("chunk," + (px + 1) + "," + (py) + ".jpeg"))
    {
        Bitmap source = new Bitmap("chunk," + (px + 1) + "," + (py) + ".jpeg");
        for (int y = 0; y < 256; y++)
        {
            seed.SetPixel(255, y, Color.FromArgb(255, source.GetPixel(0, y).R, source.GetPixel(0, y).G, source.GetPixel(0, y).B));
        }
        source.Dispose();
    }
    if (File.Exists("chunk," + (px) + "," + (py - 1) + ".jpeg"))
    {
        Bitmap source = new Bitmap("chunk," + (px) + "," + (py - 1) + ".jpeg");
        for (int x = 0; x < 256; x++)
        {
            seed.SetPixel(x, 0, Color.FromArgb(255, source.GetPixel(x, 255).R, source.GetPixel(x, 255).G, source.GetPixel(x, 255).B));
        }
        source.Dispose();
    }
    if (File.Exists("chunk," + (px) + "," + (py + 1) + ".jpeg"))
    {
        Bitmap source = new Bitmap("chunk," + (px) + "," + (py + 1) + ".jpeg");
        for (int x = 0; x < 256; x++)
        {
            seed.SetPixel(x, 255, Color.FromArgb(255, source.GetPixel(x, 0).R, source.GetPixel(x, 0).G, source.GetPixel(x, 0).B));
        }
        source.Dispose();
    }

是的,我忘记处理源变量了。另外,好像有人在我写这个答案的时候回答了。