How quickly display Minecraft Schematic to winform? - c#

There is my Code, But drawing is very slow:
int BlockWidth = 16
SourceBlueMap = new Bitmap(BlocksRect.Width, BlocksRect.Height);
Graphics Gpi = Graphics.FromImage(SourceBlueMap);
AlphaBlock block;
string strPath = #"C:\Users\Administrator\Desktop\Blocks";
BlockInfo blockinfo;
for (int x = 0; x < iXDim; x++)
{
for (int y = 0; y < iYDim; y++)
{
for (int z = 0; z < iZDim; z++)
{
block = Blocks.GetBlock(x, y, z);
rect.X = x * BlockWidth;
rect.Y = z * BlockWidth;
if (BlockInfoList.Exists(info =>
info.ID == block.ID && info.Data == block.Data))
{
blockinfo = BlockInfoList.Find(info =>
info.ID == block.ID && info.Data == block.Data);
if (File.Exists(strPath + "\\" + blockinfo.Name + ".png"))
{
using (Bitmap bmp = new Bitmap(strPath + "\\" + blockinfo.Name + ".png"))
{
Gpi.DrawImage(bmp, rect);
}
}
}
}
}
}
Gpi.Dispose();
Draw a 256 * 256 size map takes 6 seconds.
But other programs use draw than 1 second:
http://www.diamondpants.com/spritecraft/PayPalDonate.aspx

Related

Convert SetPixel to SetPixels32

I'm trying to convert my code which has SetPixel to SetPixels32. Below is my code with SetPixel
for (int x = 0; x < _brush.width; x++)
{
for (int y = 0; y < _brush.height; y++)
{
data = (pixelXOffset + x) + (pixelYOffset + y) * _templateDirtMask.width;
if (data > 0 && data < pixels.Length)
{
Color pixelDirt = _brush.GetPixel(x, y);
Color pixelDirtMask = _templateDirtMask.GetPixel(pixelXOffset + x, pixelYOffset + y);
pixels[data] = new Color(0, pixelDirtMask.g * pixelDirt.g, 0);
}
}
}
_templateDirtMask.SetPixels(pixels);
_templateDirtMask.Apply();
Then this is my code for SetPixels32
for (int x = 0; x < _brush.width; x++)
{
for (int y = 0; y < _brush.height; y++)
{
int arrElement = (pixelXOffset + x) + ((pixelYOffset + y) * heightScreenRes);
if (arrElement > 0 && arrElement < dirtMaskArr.Length)
{
Color32 pixelDirt = brushPixels[x + (y * 32)];
Color32 pixelDirtMask = dirtMaskArr[arrElement];
double pixelDirtMaskA = pixelDirtMask.g / 255.0;
double pixelDirtA = pixelDirt.g / 255.0;
double a = (pixelDirtMaskA * pixelDirtA) * 255;
dirtMaskArr[arrElement] = new Color32(0, Convert.ToByte(a), 0, 255);
}
}
}
_templateDirtMask.SetPixels32(dirtMaskArr);
_templateDirtMask.Apply(false);
My problem is it's not working.

I'm trying to do edge detection. But it works on just one image

I'm trying to create a simple edge detection filter. And as I said it works with only one image. I'm trying to create this filter with 2 steps.
Blurring image (with meanfilter)
Calculate ( Original image-Blurring image)
The first step works well. And code of second one is simple like first one. But I see an error message:
System.ArgumentOutOfRangeException: 'Parameter must be positive and < Height.
Parameter name: y'
Working image:https://i.hizliresim.com/dLXkbn.png
My code:
public void edgedetectionfilter( )
{
Bitmap InputPicture,BlurredPicture, OutputPicture;
InputPicture = new Bitmap(pBox_SOURCE.Image);
BlurredPicture = new Bitmap(pBox_PROCESSED.Image);
int PicWidth = InputPicture.Width;
int PicHeight= InputPicture.Height;
OutputPicture = new Bitmap(PicWidth, PicHeight);
OutputPicture = InputPicture;
int x, y, difR, difG, difB;
Color OrgPicColoValue,BluredPicColorValue;
for (x = 0; x < PicWidth; x++)
{
for (y = 0; y < PicWidth; y++)
{
BluredPicColorValue = BlurredPicture.GetPixel(x, y);
OrgPicColoValue = InputPicture.GetPixel(x, y); //ERROR LINE
difR = Convert.ToInt16(OrgPicColoValue.R -BluredPicColorValue.R);
difG = Convert.ToInt16(OrgPicColoValue.G- BluredPicColorValue.G );
difB = Convert.ToInt16(OrgPicColoValue.B- BluredPicColorValue.B);
if (difR > 255) difR = 255;
if (difG > 255) difG = 255;
if (difB > 255) difB = 255;
if (difR < 0) difR = 0;
if (difG < 0) difG = 0;
if (difB < 0) difB = 0;
OutputPicture.SetPixel(x, y, Color.FromArgb(difR, difG, difB));
}
}
pBoxMedian.Image = OutputPicture;
}
public void meanfilter(int p)
//KERNELSIZE=P
{
if (sliderKernel.Value % 2 == 0)
{
MessageBox.Show("Enter an odd number");
return;
}
Color ColorValue;
Bitmap InputPicture, OutputPicture;
InputPicture = new Bitmap(pBox_SOURCE.Image);
int PicWidth = InputPicture.Width;
int PicHeight= InputPicture.Height;
OutputPicture = new Bitmap(PicWidth, PicHeight);
OutputPicture = InputPicture;
int x, y, i, j, sumR, sumG, sumB, avgR, avgG, avgB;
for (x = (KernelSize - 1) / 2; x < PicWidth - (KernelSize - 1) / 2; x++)
{
for (y = (KernelSize - 1) / 2; y < PicHeight - (KernelSize- 1) / 2; y++)
{
toplamR = 0;
toplamG = 0;
toplamB = 0;
for (i = -((KernelSize - 1) / 2); i <= (KernelSize - 1) / 2; i++)
{
for (j = -((KernelSize - 1) / 2); j <= (KernelSize - 1) / 2; j++)
{
ColorValue= InputPicture.GetPixel(x + i, y + j);
sumR = sumR + ColorValue.R;
sumG = sumG + ColorValue.G;
sumB = sumB + ColorValue.B;
}
}
avgR = sumR / (KernelSize * KernelSize );
avgG = sumG / (KernelSize *KernelSize );
avgB = sumB / (KernelSize * KernelSize );
OutputPicture.SetPixel(x, y, Color.FromArgb(avgR, avgG, avgB));
}
}
pBox_PROCESSED.Image = OutputPicture;
}
You compare y < PicWidth, whereas you probably want y < PicHeight. Is the image it worked on square, by chance?

access class impossible

I created a class that I called "Descripteurs". I want to call this class to create a csv file. So I have called my list of descriptors created in the class " Descriptors ".
I can't not associate my positions of pixel with descriptors to store it in my csv.
I should have with every pixel in my image the descriptors in the class " Descripteurs"(moyenne, number of black pixel, number of white pixel.....). The csv should have the position in each pixel and all the descriptors calculated in the class "Descripteurs ". I don't know if there is a thread problem or not.
My function is here:
public bool StoreIntoCsv(string pathOriginalPicture, string pathOriginalPictureWithOnlyOneLayer, int classe, BackgroundWorker worker, DoWorkEventArgs e)
{
//preprocessing of the original picture and the original picture with only one layer
Descripteurs featuresPathOriginal = new Descripteurs(new Bitmap(pathOriginalPicture));// m_constructeur de la classe Descripteurs
Bitmap binaryOriginalPictureWithOnlyOneLayer = new Bitmap(pathOriginalPictureWithOnlyOneLayer); // correspond à l'image binaire avec le L.
//we want to get the class of the picture
string[] res = pathOriginalPicture.Split('\\');
string classeName = res[res.Count() - 2]; // count = compter
//retrieving the file path of the csv
string pathFolder = pathOriginalPicture.Split('.').First();// le split divise la chaine en sous chaine en fonction des caractères
pathFolder = pathFolder.Remove(pathFolder.LastIndexOf('\\'));
string pathCsv = pathFolder + "\\" + classeName + ".csv";
libaforge.AForge.IntPoint pixel = new libaforge.AForge.IntPoint();
//open the stream
using (StreamWriter stream = File.AppendText(pathCsv)) //streamwriter permet d'écrire dans un fichier
{
//browse all the picture
for (int x = 0; x < binaryOriginalPictureWithOnlyOneLayer.Width; x = x + 2)
{
for (int y = 0; y < binaryOriginalPictureWithOnlyOneLayer.Height; y = y + 2)
{
//the user stop the application
if (worker.CancellationPending)//checks for cancel request
{
e.Cancel = true;
return false;
}
//we know, where is it the pixel black on the data set training
if (binaryOriginalPictureWithOnlyOneLayer.GetPixel(x, y).ToArgb() == Color.Black.ToArgb())
{
pixel.X = x;
pixel.Y = y;
WriteLineToCsv(pixel, featuresPathOriginal.Extract_Desscripteurs(pixel), classe, stream);
}
}
}
return true;
}
}
My class "Descripteurs is here:
namespace ProjetPRD
{
extern alias libaforge;
public class Descripteurs
{
//private Bitmap _imageLidar;
private static Bitmap image;
public Descripteurs(Bitmap img)
{
image = img;
}
//public static List <double> Extract_Desscripteurs(Bitmap image)
//public static List <double> Extract_Desscripteurs(libaforge.AForge.IntPoint pixel)
public double[] Extract_Desscripteurs(libaforge.AForge.IntPoint pixel)
{
int pixel_Central = 0;
double Moy2_Haut_Gauche = 0;
double Moy3_Haut_Droite = 0;
double Moy4_Bas_Gauche = 0;
double Moy5_Bas_Droite = 0;
int Difference = 0; // pour calculer la difference entre la valeur max et la valeur min de notre masc
int Nb_PNoir_Haut_Gauche = 0;
int Nb_PBlanc_Haut_Gauche = 0;
int Nb_PGris_Haut_Gauche = 0;
int Nb_PNoir_Haut_Droite = 0;
int Nb_PBlanc_Haut_Droite = 0;
int Nb_PGris_Haut_Droite = 0;
int Nb_PNoir_Bas_Gauche = 0;
int Nb_PBlanc_Bas_Gauche = 0;
int Nb_PGris_Bas_Gauche = 0;
int Nb_PNoir_Bas_Droite = 0;
int Nb_PBlanc_Bas_Droite = 0;
int Nb_PGris_Bas_Droite = 0;
List<double> caracteristique = new List <double>();
lock (image )
{
BitmapData bmd = new BitmapData();
try
{
Rectangle Rect = new Rectangle(0, 0, image.Width, image.Height);
bmd = image.LockBits(Rect, ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
//System.Drawing.Imaging.BitmapData bmpData = bmd.LockBits(Rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,bmd.PixelFormat);
unsafe
{
byte* Ptr = (byte*)bmd.Scan0;
for (int j = 0; j < bmd.Height; j++)
{
for (int i = 0; i < bmd.Width; i++)
{
//initialisation des différents éléments du masque 3*3
int couleur1 = Ptr[j * bmd.Width + i];
int couleur2 = Ptr[j * bmd.Width + (i + 1)];
int couleur3 = Ptr[j * bmd.Width + (i + 2)];
int couleur4 = Ptr[(j + 1) * bmd.Width + i];
pixel_Central = Ptr[(j + 1) * bmd.Width + (i + 1)];
int couleur6 = Ptr[(j + 1) * bmd.Width + (i + 2)];
int couleur7 = Ptr[(j + 2) * bmd.Width + (i)];
int couleur8 = Ptr[(j + 2) * bmd.Width + (i + 1)];
int couleur9 = Ptr[(j + 2) * bmd.Width + (i + 2)];
//faire la moyenne de chaque bloc de 4
Moy2_Haut_Gauche = (couleur1 + couleur2 + couleur4 + pixel_Central) / 4;
Moy3_Haut_Droite = (couleur2 + couleur3 + pixel_Central + couleur6) / 4;
Moy4_Bas_Gauche = (couleur4 + pixel_Central + couleur7 + couleur8) / 4;
Moy5_Bas_Droite = (pixel_Central + couleur6 + couleur8 + couleur9) / 4;
//remplir la liste des caractéristiques
caracteristique.Add(pixel_Central);
caracteristique.Add(Moy2_Haut_Gauche);
caracteristique.Add(Moy3_Haut_Droite);
caracteristique.Add(Moy4_Bas_Gauche);
caracteristique.Add(Moy5_Bas_Droite);
int[] tab_Difference = { couleur1, couleur2, couleur3, couleur4, pixel_Central, couleur6, couleur7, couleur8, couleur9 };
Difference = tab_Difference.Max() - tab_Difference.Min();
int[] tab = { couleur1, couleur2, couleur4, pixel_Central };
for (int k = 0; k < tab.Length; k++)
{
if (tab[k] < 60)
{
Nb_PNoir_Haut_Gauche++;
}
else
{
if (tab[k] > 180)
{
Nb_PBlanc_Haut_Gauche++;
}
else
{
Nb_PGris_Haut_Gauche++;
}
}
}
int[] tab2 = { couleur2, couleur3, pixel_Central, couleur6 };
for (int m = 0; m < tab2.Length; m++)
{
if (tab2[m] < 60)
{
Nb_PNoir_Haut_Droite++;
}
else
{
if (tab2[m] > 180)
{
Nb_PBlanc_Haut_Droite++;
}
else
{
Nb_PGris_Haut_Droite++;
}
}
}
int[] tab3 = { couleur4, pixel_Central, couleur7, couleur8 };
for (int n = 0; n < tab3.Length; n++)
{
if (tab3[n] < 60)
{
Nb_PNoir_Bas_Gauche++;
}
else
{
if (tab3[n] > 180)
{
Nb_PBlanc_Bas_Gauche++;
}
else
{
Nb_PGris_Bas_Gauche++;
}
}
}
int[] tab4 = { pixel_Central, couleur6, couleur8, couleur9 };
for (int n = 0; n < tab4.Length; n++)
{
if (tab4[n] < 60)
{
Nb_PNoir_Bas_Droite++;
}
else
{
if (tab4[n] > 180)
{
Nb_PBlanc_Bas_Droite++;
}
else
{
Nb_PGris_Bas_Droite++;
}
}
}
caracteristique.Add(Difference);
caracteristique.Add(Nb_PNoir_Haut_Gauche);
caracteristique.Add(Nb_PNoir_Haut_Droite);
caracteristique.Add(Nb_PNoir_Bas_Gauche);
caracteristique.Add(Nb_PNoir_Bas_Droite);
caracteristique.Add(Nb_PBlanc_Haut_Gauche);
caracteristique.Add(Nb_PBlanc_Haut_Droite);
caracteristique.Add(Nb_PBlanc_Bas_Gauche);
caracteristique.Add(Nb_PBlanc_Bas_Droite);
caracteristique.Add(Nb_PGris_Haut_Gauche);
caracteristique.Add(Nb_PGris_Haut_Droite);
caracteristique.Add(Nb_PGris_Bas_Gauche);
caracteristique.Add(Nb_PGris_Bas_Droite);
}
}
//mesCaracteristiques = caracteristique.ToArray();
}
}
finally
{
image.UnlockBits(bmd);
}
//e.Graphics.DrawImage(bmd, 0, 150);
//return mesCaracteristiques;
return caracteristique.ToArray();
}
}
}
}
I don't know if i was clear with my explication but i really need help.

Zhang-Suen thinning algorithm C#

I am trying to write a Zhang-Suen thinning algorithm in C# following this guideline, without processing the margins.
In the function 'zhangsuen', I am reading from the image 'imgUndo' and writting to the image 'img'. The pointers dataPtrOrigin_aux inside the for cycles are used to read the 9 pixels inside a 3x3 window such that dataPtrOrigin_aux5 is the central pixel of this window, and that window will move along the whole image, moving from left to right and from top to bottom. In each pixel, if the if the statements are verified to be true, the corresponding changes are made in the image to be written by the pointer dataPtrFinal.
Note that I stored the neighbours of the current pixel inside a 8 element array. As such, they will be stored following this order:
internal static void zhangsuen(Image<Bgr, byte> img, Image<Bgr, byte> imgUndo)
{
unsafe
{
MIplImage m = img.MIplImage; //Image to be written.
MIplImage mUndo = imgUndo.MIplImage; //Image to be read.
byte* dataPtrFinal = (byte*)m.imageData.ToPointer();
byte* dataPtrUndo = (byte*)mUndo.imageData.ToPointer();
int width = img.Width; //Width of the image.
int height = img.Height; //Height of the image.
int nChan = m.nChannels; //3 channels (R, G, B).
int wStep = m.widthStep; //Total width of the image (including padding).
int padding = wStep - nChan * width; //Padding at the end of each line.
int x, y, i;
int[] neighbours = new int[8]; //Store the value of the surrounding neighbours in this array.
int step; //Step 1 or 2.
int[] sequence = { 1, 2, 4, 7, 6, 5, 3, 0, 1 };
int blackn = 0; //Number of black neighbours.
int numtransitions = 0; //Number of transitions from white to black in the sequence specified by the array sequence.
int changed = 1; //Just so it enters the while.
bool isblack = false;
int counter = 0;
while(changed > 0)
{
changed = 0;
if (counter % 2 == 0) //We want to read all the pixels in the image before going to the next step
step = 1;
else
step = 2;
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
byte* dataPtrOrigin_aux1 = (byte*)(dataPtrUndo + (y - 1) * m.widthStep + (x - 1) * m.nChannels);
byte* dataPtrOrigin_aux2 = (byte*)(dataPtrUndo + (y - 1) * m.widthStep + (x) * m.nChannels);
byte* dataPtrOrigin_aux3 = (byte*)(dataPtrUndo + (y - 1) * m.widthStep + (x + 1) * m.nChannels);
byte* dataPtrOrigin_aux4 = (byte*)(dataPtrUndo + (y) * m.widthStep + (x - 1) * m.nChannels);
byte* dataPtrOrigin_aux5 = (byte*)(dataPtrUndo + (y) * m.widthStep + (x) * m.nChannels);
byte* dataPtrOrigin_aux6 = (byte*)(dataPtrUndo + (y) * m.widthStep + (x + 1) * m.nChannels);
byte* dataPtrOrigin_aux7 = (byte*)(dataPtrUndo + (y + 1) * m.widthStep + (x - 1) * m.nChannels);
byte* dataPtrOrigin_aux8 = (byte*)(dataPtrUndo + (y + 1) * m.widthStep + (x) * m.nChannels);
byte* dataPtrOrigin_aux9 = (byte*)(dataPtrUndo + (y + 1) * m.widthStep + (x + 1) * m.nChannels);
if (x > 0 && y > 0 && x < width - 1 && y < height - 1)
{
if (dataPtrOrigin_aux5[0] == 0)
isblack = true;
if (isblack)
{
neighbours[0] = dataPtrOrigin_aux1[0];
neighbours[1] = dataPtrOrigin_aux2[0];
neighbours[2] = dataPtrOrigin_aux3[0];
neighbours[3] = dataPtrOrigin_aux4[0];
neighbours[4] = dataPtrOrigin_aux6[0];
neighbours[5] = dataPtrOrigin_aux7[0];
neighbours[6] = dataPtrOrigin_aux8[0];
neighbours[7] = dataPtrOrigin_aux9[0];
for(i = 0; i <= 7; i++)
{
if (neighbours[i] == 0)
blackn++;
if (neighbours[sequence[i]] - neighbours[sequence[i + 1]] == 255) //número de transições de branco para preto, seguindo a ordem do vector sequence
numtransitions++;
}
if ((blackn >= 2 && blackn <= 6) && numtransitions == 1)
{
if (step == 1 && (neighbours[1] == 255 || neighbours[4] == 255 || neighbours[6] == 255) && (neighbours[4] == 255 || neighbours[6] == 255 || neighbours[3] == 255))
{
dataPtrFinal[0] = 255;
dataPtrFinal[1] = 255;
dataPtrFinal[2] = 255;
changed++;
}
if (step == 2 && (neighbours[1] == 255 || neighbours[4] == 255 || neighbours[3] == 255) && (neighbours[1] == 255 || neighbours[6] == 255 || neighbours[3] == 255))
{
dataPtrFinal[0] = 255;
dataPtrFinal[1] = 255;
dataPtrFinal[2] = 255;
changed++;
}
}
}
}
dataPtrFinal += nChan;
isblack = false;
blackn = 0;
numtransitions = 0;
}
dataPtrFinal += padding;
}
dataPtrUndo = (byte*)m.imageData.ToPointer(); //Change the image to be read to the one that has just been written.
counter++;
}
}
}
As I end reading the first image and writing the changes to the image 'img' (As soon as the cycle for (y = 0; y < height; y++) ends I want the image I have just written to be the one I will read in the next cycle so that further thinning is made. I tried to accomplish this with the line
dataPtrUndo = (byte*)m.imageData.ToPointer();
Although at some value of counter that is greater than 0 (depends on the image that is read) I get an error that says that protected memory has been tried to be written which indicates I have tried to write outside of the image limits, but I don't understand why. Is it the last attribution to dataPtrUndo that I am doing erroneously?
Here is my C# implementation of Zhang-Suen thinning algorithm
public static bool[][] ZhangSuenThinning(bool[][] s)
{
bool[][] temp = s;
bool even = true;
for (int a = 1; a < s.Length-1; a++)
{
for (int b = 1; b < s[0].Length-1; b++)
{
if (SuenThinningAlg(a, b, temp, even))
{
temp[a][b] = false;
}
even = !even;
}
}
return temp;
}
static bool SuenThinningAlg(int x, int y, bool[][] s, bool even)
{
bool p2 = s[x][y - 1];
bool p3 = s[x + 1][y - 1];
bool p4 = s[x + 1][y];
bool p5 = s[x + 1][y + 1];
bool p6 = s[x][y + 1];
bool p7 = s[x - 1][y + 1];
bool p8 = s[x - 1][y];
bool p9 = s[x - 1][y - 1];
int bp1 = NumberOfNonZeroNeighbors(x, y, s);
if (bp1 >= 2 && bp1 <= 6)//2nd condition
{
if (NumberOfZeroToOneTransitionFromP9(x, y, s) == 1)
{
if (even)
{
if (!((p2 && p4) && p8))
{
if (!((p2 && p6) && p8))
{
return true;
}
}
}
else
{
if (!((p2 && p4) && p6))
{
if (!((p4 && p6) && p8))
{
return true;
}
}
}
}
}
return false;
}
static int NumberOfZeroToOneTransitionFromP9(int x, int y, bool[][]s)
{
bool p2 = s[x][y - 1];
bool p3 = s[x + 1][y - 1];
bool p4 = s[x + 1][y];
bool p5 = s[x + 1][y + 1];
bool p6 = s[x][y + 1];
bool p7 = s[x - 1][y + 1];
bool p8 = s[x - 1][y];
bool p9 = s[x - 1][y - 1];
int A = Convert.ToInt32((p2 == false && p3 == true)) + Convert.ToInt32((p3 == false && p4 == true)) +
Convert.ToInt32((p4 == false && p5 == true)) + Convert.ToInt32((p5 == false && p6 == true)) +
Convert.ToInt32((p6 == false && p7 == true)) + Convert.ToInt32((p7 == false && p8 == true)) +
Convert.ToInt32((p8 == false && p9 == true)) + Convert.ToInt32((p9 == false && p2 == true));
return A;
}
static int NumberOfNonZeroNeighbors(int x, int y, bool[][]s)
{
int count = 0;
if (s[x-1][y])
count++;
if (s[x-1][y+1])
count++;
if (s[x-1][y-1])
count++;
if (s[x][y+1])
count++;
if (s[x][y-1])
count++;
if (s[x+1][y])
count++;
if (s[x+1][y+1])
count++;
if (s[x+1][y-1])
count++;
return count;
}
bwang22's answer works. Sort of. But with two issues: It doesn't do the iterations until no more changes happen. And it does a shallow copy of the Array.. The two issues cooperate so to speak, cancelling each other out, resulting in an thinning, but not the best looking one.
Here is the corrected code, which gives a nicer looking result:
First two methods to convert from Image to bool[][] and back; the functions are not optimzed for speed; if you need that go for lockbits/unsafe..:
public static bool[][] Image2Bool(Image img)
{
Bitmap bmp = new Bitmap(img);
bool[][] s = new bool[bmp.Height][];
for (int y = 0; y < bmp.Height; y++ )
{
s[y] = new bool[bmp.Width];
for (int x = 0; x < bmp.Width; x++)
s[y][x] = bmp.GetPixel(x, y).GetBrightness() < 0.3;
}
return s;
}
public static Image Bool2Image(bool[][] s)
{
Bitmap bmp = new Bitmap(s[0].Length, s.Length);
using (Graphics g = Graphics.FromImage(bmp)) g.Clear(Color.White);
for (int y = 0; y < bmp.Height; y++)
for (int x = 0; x < bmp.Width; x++)
if (s[y][x]) bmp.SetPixel(x, y, Color.Black);
return (Bitmap)bmp;
}
Now the corrected thinning code, much of it more or less unchanged from bwang22's answer:
public static bool[][] ZhangSuenThinning(bool[][] s)
{
bool[][] temp = ArrayClone(s); // make a deep copy to start..
int count = 0;
do // the missing iteration
{
count = step(1, temp, s);
temp = ArrayClone(s); // ..and on each..
count += step(2, temp, s);
temp = ArrayClone(s); // ..call!
}
while (count > 0);
return s;
}
static int step(int stepNo, bool[][] temp, bool[][] s)
{
int count = 0;
for (int a = 1; a < temp.Length - 1; a++)
{
for (int b = 1; b < temp[0].Length - 1; b++)
{
if (SuenThinningAlg(a, b, temp, stepNo == 2))
{
// still changes happening?
if (s[a][b]) count++;
s[a][b] = false;
}
}
}
return count;
}
static bool SuenThinningAlg(int x, int y, bool[][] s, bool even)
{
bool p2 = s[x][y - 1];
bool p3 = s[x + 1][y - 1];
bool p4 = s[x + 1][y];
bool p5 = s[x + 1][y + 1];
bool p6 = s[x][y + 1];
bool p7 = s[x - 1][y + 1];
bool p8 = s[x - 1][y];
bool p9 = s[x - 1][y - 1];
int bp1 = NumberOfNonZeroNeighbors(x, y, s);
if (bp1 >= 2 && bp1 <= 6) //2nd condition
{
if (NumberOfZeroToOneTransitionFromP9(x, y, s) == 1)
{
if (even)
{
if (!((p2 && p4) && p8))
{
if (!((p2 && p6) && p8))
{
return true;
}
}
}
else
{
if (!((p2 && p4) && p6))
{
if (!((p4 && p6) && p8))
{
return true;
}
}
}
}
}
return false;
}
static int NumberOfZeroToOneTransitionFromP9(int x, int y, bool[][] s)
{
bool p2 = s[x][y - 1];
bool p3 = s[x + 1][y - 1];
bool p4 = s[x + 1][y];
bool p5 = s[x + 1][y + 1];
bool p6 = s[x][y + 1];
bool p7 = s[x - 1][y + 1];
bool p8 = s[x - 1][y];
bool p9 = s[x - 1][y - 1];
int A = Convert.ToInt32((!p2 && p3 )) + Convert.ToInt32((!p3 && p4 )) +
Convert.ToInt32((!p4 && p5 )) + Convert.ToInt32((!p5 && p6 )) +
Convert.ToInt32((!p6 && p7 )) + Convert.ToInt32((!p7 && p8 )) +
Convert.ToInt32((!p8 && p9 )) + Convert.ToInt32((!p9 && p2 ));
return A;
}
static int NumberOfNonZeroNeighbors(int x, int y, bool[][] s)
{
int count = 0;
if (s[x - 1][y]) count++;
if (s[x - 1][y + 1]) count++;
if (s[x - 1][y - 1]) count++;
if (s[x][y + 1]) count++;
if (s[x][y - 1]) count++;
if (s[x + 1][y]) count++;
if (s[x + 1][y + 1]) count++;
if (s[x + 1][y - 1]) count++;
return count;
}
I have kept the original even flag, but call it by comparing a step number. And I have saved a few characters by using the bools directly..
Finally a function to get a deep copy of the nested 2d array:
public static T[][] ArrayClone<T>(T [][] A)
{ return A.Select(a => a.ToArray()).ToArray(); }
This is how to call it, using two PictureBoxes:
pictureBox1.Image = Image.FromFile("D:\\RCdemo.png");
bool[][] t = Image2Bool(pictureBox1.Image);
t = ZhangSuenThinning(t);
pictureBox2.Image = Bool2Image(t);
I append a test image.
bwang22's answer is very slow. Try this instead:
public readonly struct ConnectivityData
{
public readonly int[] N;
public readonly int NumNeighbors;
public readonly int NumChanges;
public ConnectivityData(in int[] n, in int numNeighbors, in int numChanges)
{
N = n;
NumNeighbors = numNeighbors;
NumChanges = numChanges;
}
}
public static void ZhangSuen(in HashSet<Pixel> pixels)
{
while (true)
{
// Pass #1:
List<Pixel> mark1 = new List<Pixel>();
foreach (Pixel p in pixels)
{
ConnectivityData conn = ComputeConnectivity(p, pixels);
if (conn.NumNeighbors > 1 &&
conn.NumNeighbors < 7 &&
conn.NumChanges == 1 &&
conn.N[0] * conn.N[2] * conn.N[4] == 0 &&
conn.N[2] * conn.N[4] * conn.N[6] == 0)
{
mark1.Add(p);
}
}
//delete all marked:
foreach (Pixel p in mark1)
{
pixels.Remove(p);
}
// PASS #2:
List<Pixel> mark2 = new List<Pixel>();
foreach (Pixel p in pixels)
{
ConnectivityData conn = ComputeConnectivity(p, pixels);
if (conn.NumNeighbors > 1 &&
conn.NumNeighbors < 7 &&
conn.NumChanges == 1 &&
conn.N[0] * conn.N[2] * conn.N[6] == 0 &&
conn.N[0] * conn.N[4] * conn.N[6] == 0)
{
mark2.Add(p);
}
}
//delete all marked:
foreach (Pixel p in mark2)
{
pixels.Remove(p);
}
if (mark1.Count == 0 && mark2.Count == 0)
{
break;
}
}
}
private static ConnectivityData ComputeConnectivity(
in Pixel p,
in HashSet<Pixel> pixels)
{
// calculate #neighbors and number of changes:
int[] n = new int[8];
if (pixels.Contains(new Pixel(p.X, p.Y - 1)))
{
n[0] = 1;
}
if (pixels.Contains(new Pixel(p.X + 1, p.Y - 1)))
{
n[1] = 1;
}
if (pixels.Contains(new Pixel(p.X + 1, p.Y)))
{
n[2] = 1;
}
if (pixels.Contains(new Pixel(p.X + 1, p.Y + 1)))
{
n[3] = 1;
}
if (pixels.Contains(new Pixel(p.X, p.Y + 1)))
{
n[4] = 1;
}
if (pixels.Contains(new Pixel(p.X - 1, p.Y + 1)))
{
n[5] = 1;
}
if (pixels.Contains(new Pixel(p.X - 1, p.Y)))
{
n[6] = 1;
}
if (pixels.Contains(new Pixel(p.X - 1, p.Y - 1)))
{
n[7] = 1;
}
return new ConnectivityData(
n,
n[0] + n[1] + n[2] + n[3] + n[4] + n[5] + n[6] + n[7],
ComputeNumberOfChanges(n));
}
private static int ComputeNumberOfChanges(in int[] n)
{
int numberOfChanges = 0;
// Iterate over each location and see if it is has changed from 0 to 1:
int current = n[0];
for (int i = 1; i < 8; i++)
{
if (n[i] == 1 && current == 0)
{
numberOfChanges++;
}
current = n[i];
}
// Also consider the change over the discontinuity between n[7] and n[0]:
if (n[0] == 1 && n[7] == 0)
{
numberOfChanges++;
}
return numberOfChanges;
}
To use:
From your Bitmap etc, create a hash set of type Pixel, (which contains all the black pixels you want to thin) eg:
public class Pixel
{
public int X;
public int Y;
public Pixel(in int x, in int y)
{
X = x;
Y = y;
}
public override bool Equals(object pixel)
{
Pixel b = pixel as Pixel;
return X == b.X && Y == b.Y;
}
public override int GetHashCode()
{
//return (a.X << 2) ^ a.Y; // this is also commonly used as a pixel hash code
return X * 100000 + Y; // a bit hacky [will fail if bitmap width is > 100000]
}
}
...then call ZhangSuen(pixels). This will delete the appropriate pixels from the set.
Note that this method does not work perfectly on all images. It makes parts of some images disappear. Specifically, I am having problems with downward-right pointing diagonal lines of thickness around 11 pixels wide.
I am currently working on a way to improve this, but it performs better than the similar Staniford algorithm on most files I have tested it with (CAD files).

C# Array isn't holding data [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I'm trying to create a dungeon generator for a project I've been working on based off of this algorithm. I've gotten everything down, but my array (Fig. 1) doesn't seem to be holding giving the map data for some reason. I'm using three types of data to determine if a cell in the map is either empty (0), a space a character can be on (1), a hallway (2), or a wall (3).
I've gotten a bit stuck on this portion so any help is appreciated!
EDIT: The problem is the map object isn't storing the data in the loop shown in Fig. 1. Sorry for being so vague.
(Fig. 1)
for (int i = 0; i < roomList.Count; i++)
{
for (int x = roomList[i].X; x < (roomList[i].X + roomList[i].W); x++)
{
for (int y = roomList[i].Y; y < (roomList[i].Y + roomList[i].H); y++)
{
map[x, y] = 1;
}
}
}
(All of my relevant code)
namespace Project
{
}
public class Room
{
int xValue, yValue, widthValue, heightValue;
public int X
{
get { return xValue; }
set { xValue = value; }
}
public int Y
{
get { return yValue; }
set { yValue = value; }
}
public int W
{
get { return widthValue; }
set { widthValue = value; }
}
public int H
{
get { return heightValue; }
set { heightValue = value; }
}
}
public class DungeonGenerate
{
public int baseWidth = 513;
public int baseHeight = 513;
public int width = 64;
public int height = 64;
Color[,] arrayColor;
Random rand = new Random();
Room room = new Room();
Rectangle[,] rectMap;
public void Generate()
{
rectMap = new Rectangle[baseWidth, baseHeight];
//Creates a 2-D Array/Grid for the Dungeon
int[,] map = new int[baseWidth, baseHeight];
//Determines all the cells to be empty until otherwise stated
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
map[x, y] = 0;
}
}
//Determines the amount of rooms in the dungeon
int minRooms = (width * height) / 300;
int maxRooms = (width * height) / 150;
int amountOfRooms = rand.Next(minRooms, maxRooms);
//Room dimensions
int widthRoot = Convert.ToInt32(Math.Round(Math.Sqrt(width * 2)));
int heightRoot = Convert.ToInt32(Math.Round(Math.Sqrt(height * 2)));
int minWidth = Convert.ToInt32(Math.Round((width * .5) / widthRoot));
int maxWidth = Convert.ToInt32((width * 2) / widthRoot);
int minHeight = Convert.ToInt32(Math.Round(height * .5) / heightRoot);
int maxHeight = Convert.ToInt32((height * 2) / heightRoot);
//Creates the rooms
List<Room> roomList = new List<Room>(amountOfRooms);
for (int i = 0; i < amountOfRooms; i++)
{
bool ok = false;
do
{
room.X = rand.Next(width);
room.Y = rand.Next(height);
room.W = (rand.Next(maxWidth)) + minWidth;
room.H = (rand.Next(maxHeight)) + minHeight;
if (room.X + room.W >= width && room.Y + room.H >= height)
{
continue;
}
for (int q = 0; q < roomList.Count; q++)
{
if (room.X > roomList[q].X && room.X < roomList[q].X + room.W && room.Y > roomList[q].Y && room.Y < roomList[q].Y + room.H)
{
ok = false;
break;
}
}
ok = true;
roomList.Add(room);
} while (!ok);
}
//This will create hallways that lead to and from the rooms
int connectionCount = roomList.Count;
List<Point> connectedCells = new List<Point>((width * height));
for (int i = 0; i < connectionCount; i++)
{
Room roomA = roomList[i];
int roomNum = i;
while (roomNum == i)
{
roomNum = rand.Next(roomList.Count);
}
Room roomB = roomList[roomNum];
//Increasing this will make the hallway more straight, decreasing it will make the hallway more skewed
int sidestepChance = 10;
Point pointA = new Point(x: (rand.Next(roomA.W)) + roomA.X, y: (rand.Next(roomA.H)) + roomA.Y);
Point pointB = new Point(x: (rand.Next(roomB.W)) + roomB.X, y: (rand.Next(roomB.H)) + roomB.Y);
while (pointA != pointB)
{
int num = rand.Next() * 100;
if (num < sidestepChance)
{
if (pointB.X != pointA.X)
{
if (pointB.X > pointA.X)
{
pointB.X--;
}
else
{
pointB.X++;
}
}
}
else if(pointB.Y != pointA.Y)
{
if (pointB.Y > pointA.Y)
{
pointB.Y--;
}
else
{
pointB.Y++;
}
}
}
if (pointB.X < width && pointB.Y < height)
{
connectedCells.Add(pointB);
}
}
//Fills the room with data
for (int i = 0; i < roomList.Count; i++)
{
for (int x = roomList[i].X; x < (roomList[i].X + roomList[i].W); x++)
{
for (int y = roomList[i].Y; y < (roomList[i].Y + roomList[i].H); y++)
{
map[x, y] = 1;
}
}
}
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (map[x, y] == 0)
{
bool wall = false;
for (int yy = y - 2; yy < y + 2; yy++)
{
for (int xx = x - 2; xx < x + 2; xx++)
{
if (xx > 0 && yy > 0 && xx < width && yy < height)
{
if (map[xx, yy] == 1 || map[xx, yy] == 2)
{
map[x, y] = 3;
wall = true;
}
}
}
if (wall)
{
break;
}
}
}
}
}
//Rendering the Map and giving it some Color (Sort of)!
int scaler = baseWidth / width;
for (int x = 0; x < baseWidth; x++)
{
for (int y = 0; y < baseHeight; y++)
{
rectMap[x, y] = new Rectangle(x, y, 1, 1);
arrayColor = new Color[baseWidth, baseHeight];
switch (map[x, y])
{
case 0:
arrayColor[x, y] = new Color(0,0,0);
break;
case 1:
arrayColor[x, y] = new Color(0,0,0);
break;
case 2:
arrayColor[x, y] = new Color(0,0,0);
break;
case 3:
arrayColor[x, y] = new Color (0,0,0);
break;
}
}
}
}
public Rectangle[,] GetMap()
{
return rectMap;
}
public Color[,] GetColors()
{
return arrayColor;
}
}
In the for-loop where you're populating roomList, you're not instantiating a new Room each time. You're simply manipulating the same Room object and re-adding it to the list, so roomList will just contain many references to the same Room object. Try removing the room field from your DungeonGenerate class and use a local variable instead:
for (int i = 0; i < amountOfRooms; i++)
{
bool ok = false;
do
{
var room = new Room();
...
roomList.Add(room);
} while (!ok);
}

Categories

Resources