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
而不是 GetPixel
和 SetPixel
并且你会得到更好的性能因素
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();
}
是的,我忘记处理源变量了。另外,好像有人在我写这个答案的时候回答了。
所以,我正在构建一个控制台应用程序来生成 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
而不是 GetPixel
和 SetPixel
并且你会得到更好的性能因素
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();
}
是的,我忘记处理源变量了。另外,好像有人在我写这个答案的时候回答了。