So, I'm building a console application to generate 2D perlin(ish) noise that is split among multiple image files. The algorithm I'm using requires that I do the generation in two passes, one to create the seed for the noise, then to generate the noise. I'm saving the generated noise to the same file that the seed is generated to in order to cut down on disk-space, and to make it clean in the end. I'm running into an issue though, during the second pass, I can't actually access the file, as it's already being used by another procces. Here's the full code to see if there is anything that I'm missing:
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);
}
}
}
}
}
As you can see, I've tried a lot of disposing of the variables, but to no avail. Any ideas what I'm doing wrong? The odd thing is, when it was only a single function running, I could freely overwrite the base image file, but now that I split it up into two different functions, it's all downhill, and it won't let me manipulate the files.
You need to dispose your bitmaps, as below
Also i would use Lockbits instead of GetPixel and SetPixel and you will get factors better performance
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);
}
}
Example how to use LockBits and pointers using the unsafe keyword. Basically you are accessing your picture by scan lines in a pinned array using pointers directly accessing the 32 bit ARBG values in memory
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();
}
Yeah, I had forgotten to dispose the source variable. also, seems like someone had answered while I was writing this answer.
I'm writing a game in XNA, created simple method to get subImages from textures, but everytime I use it, it throws an exception. I checked the variables and there is no chance to get out of bounds. Code for this two methods below:
public Color[] GetSubImage(Color[] colorData, int width, Rectangle rec)
{
Color[] color = new Color[rec.Width * rec.Height];
for (int x = 0; x < rec.Width; x++)
{
for (int y = 0; y < rec.Height; y++)
{
color[x + y * rec.Width] = colorData[x + rec.X + (y + rec.Y) * width]; // Exception is thrown there
}
}
return color;
}
public void LoadSubImages(Texture2D sourceSpritesheet, List<Texture2D[]> destinationSprites)
{
int count = 0;
Color[] imageData = new Color[sourceSpritesheet.Width * sourceSpritesheet.Height];
Texture2D subImage;
Rectangle sourceRec;
destinationSprites = new List<Texture2D[]>();
for (int i = 0; i < this.NUMFRAMES.Length; i++)
{
Texture2D[] bi = new Texture2D[this.NUMFRAMES[i]];
for (int j = 0; j < this.NUMFRAMES[i]; j++)
{
sourceRec = new Rectangle(j * this.FRAMEWIDTHS[i], count, this.FRAMEWIDTHS[i], this.FRAMEHEIGHTS[i]);
Color[] imagePiece = this.GetSubImage(imageData, sourceSpritesheet.Width, sourceRec);
subImage = new Texture2D(Game1.Instance.GraphicsDevice, sourceRec.Width, sourceRec.Height);
subImage.SetData<Color>(imagePiece);
bi[j] = subImage;
}
destinationSprites.Add(bi);
count += this.FRAMEHEIGHTS[i];
}
}
sourceSpritesheet is 368*550 big, FRAMEWIDTHS = 46, FRAMEHEIGTHS = 50, NUMFRAMES.Length = 11 (with values between 1-8)
Is there something that I can't see?
colorData has indices starting from 0 to width * height. You're accessing indices starting from rec.X + rec.Y * width to (rec.X + width) + (height + rec.Y) * height. If rec.X or rec.Y is greater than 0 (which will happen, given how you construct your rectangles), this will go out of bounds. .NET Framework arrays are luckily working correctly, universe is safe...
colorData is 202,400 in size
In the worst case scenario :
colorData[x + rec.X + (y + rec.Y) * width];
x = 45
rec.x = 7*46 = 322
y = 50
rec.y = 11*50 = 550
width = 368
due to the order of operations your formula would execute like so:
x + rec.X + ((y + rec.Y) * width)
45 + 322 + ((50 + 550) * 368)
367 + (600 * 368)
221,167
and 221,167 is greater then colorData size of 202,400. So in conclusion it is most definitely possible to go out of bounds with your function. I would recommend you rewrite it as it seems to be a horrid case of spaghetti code.
I have a little problem in my Unity work, I'm trying to get the images from a Webcam texture, and I'll keep it in a byte []. the problem comes when I show the image, which is not seen correctly, it looks like some type of grid mesh. I think the problem is in the for I use to pass the pixels. I hope you can help me.
void Update()
{
//texto.text = texto.text + width +" / "+ height + " / " + ini.ToString()+ " mal: "+ testo().ToString()+ " SizeIMG: "+ imgData.Length + " NUEVO: "+ nuevo +"\n";
imgData = null;
imgData = new byte[width * height * 3];
resultado = null;
resultado = new byte[width * height * 3];
color = webcam.GetPixels32();
for (int i = 0; i < color.Length; i += 3)
{
imgData[(i * 3)] = color[i].r;
imgData[(i * 3) + 1] = color[i].g;
imgData[(i * 3) + 2] = color[i].b;
}
color = null;
//video(imgData, resultado);
//ProcessFrame(imgData, resultado, 0, 0, 0,0, nuevo);
nuevo = false;
textura2 = new Texture2D(width, height, TextureFormat.RGB24, false);
textura2.LoadRawTextureData(imgData);
textura2.Apply();
//Left IMAGE
renderer.material.mainTexture = textura2;
textura2 = null;
RightImage.GetComponent<Renderer>().material.mainTexture = webcam;
if (kont == 30)
{
texto.text = "";
kont = 0;
}
kont++;
Resources.UnloadUnusedAssets();
}
I think the issue is how your indexing into your imgData array. You're incrementing i by three in addition to multiplying by 3.
int bytesPerPixel = 3;
const int indexR = 0;
const int indexG = 1;
const int indexB = 2;
for(var i = 0; i < color.Length; i++)
{
imgData[(i * bytesPerPixel) + indexR] = color[i].r;
imgData[(i * bytesPerPixel) + indexG] = color[i].g;
imgData[(i * bytesPerPixel) + indexB] = color[i].b;
}
I found some great code here on SO and have implemented it in my app and although it is "working", the blur effect is not strong enough to actually realise (without focusing very hard on it) that it has actually blurred anything.
The code is (quite long, sorry):
Bitmap screenshot = null;
void BlurForm()
{
/*
The best time (I assume) is to change the blur when the user as left the window,
as opposed to doing it when they want to open the window, since this may be a
little intensive.
*/
screenshot = null;
blurred.Image = null; // This variable is the PictureBox that's on the Form
screenshot = Screenshot.TakeSnapshot(this);
BitmapFilter.GaussianBlur(screenshot, 4);
blurred.Image = screenshot;
blurred.SendToBack();
}
public class ConvMatrix
{
public int TopLeft = 0, TopMid = 0, TopRight = 0;
public int MidLeft = 0, Pixel = 1, MidRight = 0;
public int BottomLeft = 0, BottomMid = 0, BottomRight = 0;
public int Factor = 1;
public int Offset = 0;
public void SetAll(int nVal)
{
TopLeft = TopMid = TopRight = MidLeft = Pixel = MidRight = BottomLeft = BottomMid = BottomRight = nVal;
}
}
public class BitmapFilter
{
private static bool Conv3x3(Bitmap b, ConvMatrix m)
{
// Avoid divide by zero errors
if (0 == m.Factor) return false;
Bitmap bSrc = (Bitmap)b.Clone();
// GDI+ still lies to us - the return format is BGR, NOT RGB.
BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
BitmapData bmSrc = bSrc.LockBits(new Rectangle(0, 0, bSrc.Width, bSrc.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int stride = bmData.Stride;
int stride2 = stride * 2;
System.IntPtr Scan0 = bmData.Scan0;
System.IntPtr SrcScan0 = bmSrc.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
byte* pSrc = (byte*)(void*)SrcScan0;
int nOffset = stride + 6 - b.Width * 3;
int nWidth = b.Width - 2;
int nHeight = b.Height - 2;
int nPixel;
for (int y = 0; y < nHeight; ++y)
{
for (int x = 0; x < nWidth; ++x)
{
nPixel = ((((pSrc[2] * m.TopLeft) + (pSrc[5] * m.TopMid) + (pSrc[8] * m.TopRight) +
(pSrc[2 + stride] * m.MidLeft) + (pSrc[5 + stride] * m.Pixel) + (pSrc[8 + stride] * m.MidRight) +
(pSrc[2 + stride2] * m.BottomLeft) + (pSrc[5 + stride2] * m.BottomMid) + (pSrc[8 + stride2] * m.BottomRight)) / m.Factor) + m.Offset);
if (nPixel < 0) nPixel = 0;
if (nPixel > 255) nPixel = 255;
p[5 + stride] = (byte)nPixel;
nPixel = ((((pSrc[1] * m.TopLeft) + (pSrc[4] * m.TopMid) + (pSrc[7] * m.TopRight) +
(pSrc[1 + stride] * m.MidLeft) + (pSrc[4 + stride] * m.Pixel) + (pSrc[7 + stride] * m.MidRight) +
(pSrc[1 + stride2] * m.BottomLeft) + (pSrc[4 + stride2] * m.BottomMid) + (pSrc[7 + stride2] * m.BottomRight)) / m.Factor) + m.Offset);
if (nPixel < 0) nPixel = 0;
if (nPixel > 255) nPixel = 255;
p[4 + stride] = (byte)nPixel;
nPixel = ((((pSrc[0] * m.TopLeft) + (pSrc[3] * m.TopMid) + (pSrc[6] * m.TopRight) +
(pSrc[0 + stride] * m.MidLeft) + (pSrc[3 + stride] * m.Pixel) + (pSrc[6 + stride] * m.MidRight) +
(pSrc[0 + stride2] * m.BottomLeft) + (pSrc[3 + stride2] * m.BottomMid) + (pSrc[6 + stride2] * m.BottomRight)) / m.Factor) + m.Offset);
if (nPixel < 0) nPixel = 0;
if (nPixel > 255) nPixel = 255;
p[3 + stride] = (byte)nPixel;
p += 3;
pSrc += 3;
}
p += nOffset;
pSrc += nOffset;
}
}
b.UnlockBits(bmData);
bSrc.UnlockBits(bmSrc);
return true;
}
public static bool GaussianBlur(Bitmap b, int nWeight /* default to 4*/)
{
ConvMatrix m = new ConvMatrix();
m.SetAll(1);
m.Pixel = nWeight;
m.TopMid = m.MidLeft = m.MidRight = m.BottomMid = 2;
m.Factor = nWeight + 12;
return BitmapFilter.Conv3x3(b, m);
}
}
class Screenshot
{
public static Bitmap TakeSnapshot(Control ctl)
{
Bitmap bmp = new Bitmap(ctl.Size.Width, ctl.Size.Height);
using (Graphics g = System.Drawing.Graphics.FromImage(bmp))
{
g.CopyFromScreen(
ctl.PointToScreen(ctl.ClientRectangle.Location),
new Point(0, 0), ctl.ClientRectangle.Size
);
}
return bmp;
}
}
I've tried changing every single value in there and it doesn't matter which value(s) I change, I just can't get it to have a stronger blur effect. How can I increase the strength of the Gaussian Blur?
The Form is 80% Opaque, so the blur effect you would see is the windows wallpaper or whatever is behind the form - but blurred. Only problem (as I've said) is that the blur is not strong at all.
After just glancing at the code, it looks to me like the blur filter is hardcoded to only operate in a 3x3 area at a time, severely restricting how blurred it can make an image.
Unless I'm mistaken, the only way to significantly increase the amount of blurring is to find a more sophisticated implementation.