I was trying to create simple program that would perform some trivial operation with given image.
For some reason, it works only for the first time (when the app is launched):
//pseudo code
Bitmap im=Bitmap.FromFile("D:\\x.BMP");
Color [,] ColorArray=new [im.Width,im.Height];
private override voide OnPaint(EventArgs e)
{
for(int X=0;X<im.Width;X++)
{
for(int Y=0;Y<im.Height;Y++)
{
ColorArray[X,Y]=im.GetPixel[X,Y];
}
}
for(int X=0;X<im.Width;X++)
{
for(int Y=0;Y<im.Height;Y++)
{
Color c=ColorArray[X,Y];
...
//some code that adds 100 to R,G,B
im.SetPixel(X,Y,c);
}
}
e.Graphics.DrawImage(im);
}
[EDIT]
I have modified my sample code. With the previous version, it could be hard to see the difference from one iteration to the next (especially with the bmp I was using for testing). This one allows you to change color gradually by pressing 1, 2, or 3 on the Num Pad. As others have mentioned, SetPixel/GetPixel is slow, so each KeyUp event does take some time to run.
This works fine for me.
Create a new Windows Forms project. Drop this code in. Make sure you have "x.bmp" available. Each time you press 1, 2, or 3 on the number pad (you might need turn on Num Lock), the colors of the bitmap change slightly:
public partial class Form1 : Form
{
Bitmap bm;
Color [,] colors;
public Form1()
{
bm = (Bitmap)Bitmap.FromFile("x.bmp");
colors = new Color[bm.Width, bm.Height];
InitializeComponent();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(bm,new Point(0,0));
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
//Change the colors a little bit on each KeyUp.
//On NumPad1, change red.
//On NumPad2, change green.
//On NumPad3, change blue.
//Mod with 256 to ensure a value between 0 and 255.
int rInc = 0;
int gInc = 0;
int bInc = 0;
switch (e.KeyCode)
{
case Keys.NumPad1:
rInc = 20;
break;
case Keys.NumPad2:
gInc = 20;
break;
case Keys.NumPad3:
bInc = 20;
break;
default:
break;
}
for (int x = 0; x < bm.Width; x++)
{
for (int y = 0; y < bm.Height; y++)
{
colors[x, y] = bm.GetPixel(x, y);
}
}
for (int x = 0; x < bm.Width; x++)
{
for (int y = 0; y < bm.Height; y++)
{
Color c = colors[x, y];
int r = (c.R + rInc) % 256;
int g = (c.G + gInc) % 256;
int b = (c.B + bInc) % 256;
c = Color.FromArgb(255, r, g, b);
bm.SetPixel(x, y, c);
}
}
Invalidate();
}
}
It's not very sophisticated, but it does what you asked with real code that closely matches your pseudocode.
You can improve your performance when looping over pixels, arrange the loops to access the pixels in row order (x coordinates) in the inner loop. That best matches the image memory layout so it will improve CPU cache performance
for ( y = 0, y < bm.Height; y++)
{
...
for ( x = 0, x < bm.Width; x++)
{
...
}
}
Related
I've been trying to create a bitmap, and use said bitmap to create an image which needs to be shown inside a picturebox. So far Google hasn't been of any help. The bitmap needs to be filled with black/white pixels defined in an array, but I've used Aliceblue for now.
When I run the code I get the error "value cant be null" at this line
Bitmap afbeelding = new Bitmap(resolutie, resolutie, g);
Here's what I've tried:
public void draw(Array array)
{
Bitmap afbeelding = new Bitmap(resolutie, resolutie, g);
for(int x = 0; x < array.Length; x++)
{
for (int y = 0; y < array.Length; y++)
{
afbeelding.SetPixel(x, y, Color.AliceBlue);
}
}
pictureBox1.Image = afbeelding;
//afbeelding = pictureBox1.CreateGraphics();
}
Does anyone know how to solve this? I'm not sure how to fill g since there isn't a DrawPixel function in Graphics
Assuming the array contains the definition of an image, you should probably fill the rows of the image with a chunk of the array, instead of filling the image horizontally and vertically with the array.
Let's suppose the array is for a 10 x 10 image, which would make the array 100 bytes long. You need to assign the first 10 bytes to the first row of the image, and so on. You also need to check the value of the array member whether to draw a pixel or not.
Example:
public void draw(bool[] array)
{
Bitmap afbeelding = new Bitmap(resolutieX, resolutieY);
for(int y = 0; y < resolutieY; y++)
{
for (int x = 0; x < resolutieX; x++)
{
if (array[y * resolutieX + x] == true)
afbeelding.SetPixel(x, y, Color.Black);
else
afbeelding.SetPixel(x, y, Color.White);
}
}
pictureBox1.Image = afbeelding;
}
To test this (assuming you have a button1 on the form):
int resolutieX = 100;
int resolutieY = 100;
Random R = new Random();
private void button1_Click(object sender, EventArgs e)
{
bool[] bArray = new bool[resolutieX * resolutieY];
for (int i = 0; i < bArray.Length; i++)
bArray[i] = R.Next(0, 2) == 1 ? true : false;
draw(bArray);
}
public void draw(int[] array)
{
Bitmap afbeelding = new Bitmap(11, 11);
for (int i = 0; i < array.Length; i++)
{
afbeelding.SetPixel(array[i], array[i], Color.Black);
}
pictureBox1.Image = afbeelding;
//afbeelding = pictureBox1.CreateGraphics();
}
private void Form1_Load(object sender, EventArgs e)
{
draw(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
}
Why not locking the pixel array? Check this out, it is faster:
BitmapData Class
Necessary to draw by pixels array with data (large). How can I do that?
I tried a Canvas and Rectangle on it - the computer hanged himself ...
Tried the following options below (DrawingContext) - computer still hangs himself, but a little bit less.
Please recommend options with the least load on the computer.
int width = 800; - the size of the array (large!!!)
int size = 1; - it is desirable to be able to make the fragment is not only one but several pixels
protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
{
Random rnd = new Random();
int width = 800;
int size = 1;
CellType[,] types = new CellType[width, width];
for (int i = 0; i < width; i++)
{
for (int j = 0; j < width; j++)
{
int r = rnd.Next(0, 100);
if (r >= 70) types[j,i] = CellType.IsOccupied;
else types[j, i] = CellType.IsEmpty;
}
}
for (int i = 0; i < width; i++)
{
for (int j = 0; j < width; j++)
{
Brush brush = Brushes.Black;
switch (types[j, i])
{
case CellType.IsEmpty: brush = Brushes.Green;
break;
case CellType.IsOccupied: brush = Brushes.Black;
break;
}
drawingContext.DrawRectangle(brush,
new Pen(brush, 1),
new Rect(j * size, i * size, size, size));
}
}
base.OnRender(drawingContext);
}
For how long does it freeze? A couple of years ago I did something quite similar in Silverlight. It was a bit slow but still acceptable. Try using WriteableBitmap with SetPixel or FillRectangle methods, but please keep in mind that drawing pixel by pixel in a loop will always take some time.
Forgot to mention SetPixel and FillRectangle may need WriteableBitmapEx, available here:
http://writeablebitmapex.codeplex.com/
Im doing a normalization for my image and i've already gotten done the RGB values for the image. can anyone help me with the RGB normalization for the c# codes like maybe some starters on how to write out the normalization code. thanks!
private void normalization_Click(object sender, EventArgs e)
{
// for (int x = 0; x < pictureBox1.Width; x++)
{
// for (int y = 0; y < pictureBox1.Height; y++)
{
try
{
Bitmap img = new Bitmap(pictureBox1.Image);
Color c;
for (int i = 0; i < img.Width; i++)
{
for (int j = 0; j < img.Height; j++)
{
c = img.GetPixel(i, j);
int r = Convert.ToInt16(c.R);
int g = Convert.ToInt16(c.G);
int b = Convert.ToInt16(c.B);
d = r / (r + g + b);
h = g / (r + g + b);
f = b / (r + g + b);
img.SetPixel(i, j, Color.FromArgb(d, h, f));
Color pixelColor = img.GetPixel(i, j);
float normalizedPixel = (d + h + f);
Color normalizedPixelColor = System.Drawing.ColorConverter.(normalizedPixel);
img.SetPixel(x, y, normalizedPixelColor);
}
}
}
catch (Exception ex) { }
Hi. I've done the formulaes and all for normalization. The problem that I am facing now is that I'm having trouble getting the values of the RGB pixels out of the listbox and normalizing it with the formulae and then putting the pixels back into picturebox/image. Above is the code that i've tried to do to put the normalized RGB pixels back into the picturebox. Can I ask for some help with this because it still doesn't normalize my image. Thanks.
Have a look at AForge.NET if you do not mind having a dependency in your project. As a bonus you'll have plenty of other algorithms at your availability without the need for you to re-invent the for loop...
How do I use a background worker in this for loop?
int tmax = 10;
int xmax = newbitmap.Width;
int ymax = newbitmap.Height;
for (int t = 0; t <= tmax; t += 1)
{
for (int x = 0; x < xmax; x++)
{
for (int y = 0; y < ymax; y++)
{
if ((x / xmax) > (t / tmax))
{
Color originalco = newbitmap2.GetPixel(x, y);
outp.SetPixel(x, y, originalco);
}
else
{
Color originalco3 = newbitmap.GetPixel(x, y); ;
outp.SetPixel(x, y, originalco3);
}
}
pictureBox1.Image = outp;
}
}
This loop is a wipe transition from right to left, but it doesn't display the transition.
That because the backgroundWorker works in a different thread. You can use backgroundworker.ReportProgress(0, outp)
So:
You need to register to the event BackgroundWorker.ProgressChanged from the events window in Vistual Studio, or with this line:
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
The method:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
var outp = (Bitmap)e.UserState;
prictureBox.Image = outp;
}
Your code sould be then:
int tmax = 10;
int xmax = newbitmap.Width;
int ymax = newbitmap.Height;
for (int t = 0; t <= tmax; t += 1)
{
for (int x = 0; x < xmax; x++)
{
for (int y = 0; y < ymax; y++)
{
if ((x / xmax) > (t / tmax))
{
Color originalco = newbitmap2.GetPixel(x, y);
outp.SetPixel(x, y, originalco);
}
else
{
Color originalco3 = newbitmap.GetPixel(x, y); ;
outp.SetPixel(x, y, originalco3);
}
}
backgroundWorker1.ReportProgress(t, outp);
}
}
First, you should use direct pixel manipulation as described here: http://msdn.microsoft.com/en-us/library/5ey6h79d.aspx
Then, you should use an array as lookup for all your threads which line has already been drawn and which hasn't. The threads look for a new line in this array and then draw it. But remember to lock the lookup array!
Your Do work method would look something like this: -
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
Bitmap newbitmap = (Bitmap)e.Argument;
int tmax = 10;
int xmax = newbitmap.Width;
int ymax = newbitmap.Height;
for (int t = 0; t <= tmax; t += 1)
{
for (int x = 0; x < xmax; x++)
{
for (int y = 0; y < ymax; y++)
{
if ((x / xmax) > (t / tmax))
{
Color originalco = newbitmap2.GetPixel(x, y);
outp.SetPixel(x, y, originalco);
}
else
{
Color originalco3 = newbitmap.GetPixel(x, y); ;
outp.SetPixel(x, y, originalco3);
}
}
pictureBox1.Image = outp;
}
}
bgWorker.ReportProgress(0,outp);
}
Then when your worker reports progress, it would raise the following event where you can safely update the UI:
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//UPDATE YOUR UI HERE
}
You can use the ReportProgress method of Background Worker to update the UI.
Read more
I'm attempting to add semi-realistic water into my tile-based, 2D platformer. The water must act somewhat lifelike, with a pressure model that runs entirely local. (IE. Can only use data from cells near it) This model is needed because of the nature of my game, where you cannot be certain that the data you need isn't inside an area that isn't in memory.
I've tried one method so far, but I could not refine it enough to work with my constraints.
For that model, each cell would be slightly compressible, depending on the amount of water in the above cell. When a cell's water content was larger than the normal capacity, the cell would try to expand upwards. This created a fairly nice simulation, abeit slow (Not lag; Changes in the water were taking a while to propagate.), at times. When I tried to implement this into my engine, I found that my limitations lacked the precision required for it to work. I can provide a more indepth explanation or a link to the original concept if you wish.
My constraints:
Only 256 discrete values for water level. (No floating point variables :( ) -- EDIT. Floats are fine.
Fixed grid size.
2D Only.
U-Bend Configurations must work.
The language that I'm using is C#, but I can probably take other languages and translate it to C#.
The question is, can anyone give me a pressure model for water, following my constraints as closely as possible?
How about a different approach?
Forget about floats, that's asking for roundoff problems in the long run. Instead, how about a unit of water?
Each cell contains a certain number of units of water. Each iteration you compare the cell with it's 4 neighbors and move say 10% (change this to alter the propagation speed) of the difference in the number of units of water. A mapping function translates the units of water into a water level.
To avoid calculation order problems use two values, one for the old units, one for the new. Calculate everything and then copy the updated values back. 2 ints = 8 bytes per cell. If you have a million cells that's still only 8mb.
If you are actually trying to simulate waves you'll need to also store the flow--4 values, 16 mb. To make a wave put some inertia to the flow--after you calculate the desired flow then move the previous flow say 10% of the way towards the desired value.
Try treating each contiguous area of water as a single area (like flood fill) and track 1) the lowest cell(s) where water can escape and 2) the highest cell(s) from which water can come, then move water from the top to the bottom. This isn't local, but I think you can treat the edges of the area you want to affect as not connected and process any subset that you want. Re-evaluate what areas are contiguous on each frame (re-flood on each frame) so that when blobs converge, they can start being treated as one.
Here's my code from a Windows Forms demo of the idea. It may need some fine tuning, but seems to work quite well in my tests:
public partial class Form1 : Form
{
byte[,] tiles;
const int rows = 50;
const int cols = 50;
public Form1()
{
SetStyle(ControlStyles.ResizeRedraw, true);
InitializeComponent();
tiles = new byte[cols, rows];
for (int i = 0; i < 10; i++)
{
tiles[20, i+20] = 1;
tiles[23, i+20] = 1;
tiles[32, i+20] = 1;
tiles[35, i+20] = 1;
tiles[i + 23, 30] = 1;
tiles[i + 23, 32] = 1;
tiles[21, i + 15] = 2;
tiles[21, i + 4] = 2;
if (i % 2 == 0) tiles[22, i] = 2;
}
tiles[20, 30] = 1;
tiles[20, 31] = 1;
tiles[20, 32] = 1;
tiles[21, 32] = 1;
tiles[22, 32] = 1;
tiles[33, 32] = 1;
tiles[34, 32] = 1;
tiles[35, 32] = 1;
tiles[35, 31] = 1;
tiles[35, 30] = 1;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (SolidBrush b = new SolidBrush(Color.White))
{
for (int y = 0; y < rows; y++)
{
for (int x = 0; x < cols; x++)
{
switch (tiles[x, y])
{
case 0:
b.Color = Color.White;
break;
case 1:
b.Color = Color.Black;
break;
default:
b.Color = Color.Blue;
break;
}
e.Graphics.FillRectangle(b, x * ClientSize.Width / cols, y * ClientSize.Height / rows,
ClientSize.Width / cols + 1, ClientSize.Height / rows + 1);
}
}
}
}
private bool IsLiquid(int x, int y)
{
return tiles[x, y] > 1;
}
private bool IsSolid(int x, int y)
{
return tiles[x, y] == 1;
}
private bool IsEmpty(int x, int y)
{
return IsEmpty(tiles, x, y);
}
public static bool IsEmpty(byte[,] tiles, int x, int y)
{
return tiles[x, y] == 0;
}
private void ProcessTiles()
{
byte processedValue = 0xFF;
byte unprocessedValue = 0xFF;
for (int y = 0; y < rows; y ++)
for (int x = 0; x < cols; x++)
{
if (IsLiquid(x, y))
{
if (processedValue == 0xff)
{
unprocessedValue = tiles[x, y];
processedValue = (byte)(5 - tiles[x, y]);
}
if (tiles[x, y] == unprocessedValue)
{
BlobInfo blob = GetWaterAt(new Point(x, y), unprocessedValue, processedValue, new Rectangle(0, 0, 50, 50));
blob.ProcessMovement(tiles);
}
}
}
}
class BlobInfo
{
private int minY;
private int maxEscapeY;
private List<int> TopXes = new List<int>();
private List<int> BottomEscapeXes = new List<int>();
public BlobInfo(int x, int y)
{
minY = y;
maxEscapeY = -1;
TopXes.Add(x);
}
public void NoteEscapePoint(int x, int y)
{
if (maxEscapeY < 0)
{
maxEscapeY = y;
BottomEscapeXes.Clear();
}
else if (y < maxEscapeY)
return;
else if (y > maxEscapeY)
{
maxEscapeY = y;
BottomEscapeXes.Clear();
}
BottomEscapeXes.Add(x);
}
public void NoteLiquidPoint(int x, int y)
{
if (y < minY)
{
minY = y;
TopXes.Clear();
}
else if (y > minY)
return;
TopXes.Add(x);
}
public void ProcessMovement(byte[,] tiles)
{
int min = TopXes.Count < BottomEscapeXes.Count ? TopXes.Count : BottomEscapeXes.Count;
for (int i = 0; i < min; i++)
{
if (IsEmpty(tiles, BottomEscapeXes[i], maxEscapeY) && (maxEscapeY > minY))
{
tiles[BottomEscapeXes[i], maxEscapeY] = tiles[TopXes[i], minY];
tiles[TopXes[i], minY] = 0;
}
}
}
}
private BlobInfo GetWaterAt(Point start, byte unprocessedValue, byte processedValue, Rectangle bounds)
{
Stack<Point> toFill = new Stack<Point>();
BlobInfo result = new BlobInfo(start.X, start.Y);
toFill.Push(start);
do
{
Point cur = toFill.Pop();
while ((cur.X > bounds.X) && (tiles[cur.X - 1, cur.Y] == unprocessedValue))
cur.X--;
if ((cur.X > bounds.X) && IsEmpty(cur.X - 1, cur.Y))
result.NoteEscapePoint(cur.X - 1, cur.Y);
bool pushedAbove = false;
bool pushedBelow = false;
for (; ((cur.X < bounds.X + bounds.Width) && tiles[cur.X, cur.Y] == unprocessedValue); cur.X++)
{
result.NoteLiquidPoint(cur.X, cur.Y);
tiles[cur.X, cur.Y] = processedValue;
if (cur.Y > bounds.Y)
{
if (IsEmpty(cur.X, cur.Y - 1))
{
result.NoteEscapePoint(cur.X, cur.Y - 1);
}
if ((tiles[cur.X, cur.Y - 1] == unprocessedValue) && !pushedAbove)
{
pushedAbove = true;
toFill.Push(new Point(cur.X, cur.Y - 1));
}
if (tiles[cur.X, cur.Y - 1] != unprocessedValue)
pushedAbove = false;
}
if (cur.Y < bounds.Y + bounds.Height - 1)
{
if (IsEmpty(cur.X, cur.Y + 1))
{
result.NoteEscapePoint(cur.X, cur.Y + 1);
}
if ((tiles[cur.X, cur.Y + 1] == unprocessedValue) && !pushedBelow)
{
pushedBelow = true;
toFill.Push(new Point(cur.X, cur.Y + 1));
}
if (tiles[cur.X, cur.Y + 1] != unprocessedValue)
pushedBelow = false;
}
}
if ((cur.X < bounds.X + bounds.Width) && (IsEmpty(cur.X, cur.Y)))
{
result.NoteEscapePoint(cur.X, cur.Y);
}
} while (toFill.Count > 0);
return result;
}
private void timer1_Tick(object sender, EventArgs e)
{
ProcessTiles();
Invalidate();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
int x = e.X * cols / ClientSize.Width;
int y = e.Y * rows / ClientSize.Height;
if ((x >= 0) && (x < cols) && (y >= 0) && (y < rows))
tiles[x, y] = 2;
}
}
}
From a fluid dynamics viewpoint, a reasonably popular lattice-based algorithm family is the so-called Lattice Boltzmann method. A simple implementation, ignoring all the fine detail that makes academics happy, should be relatively simple and fast and also get reasonably correct dynamics.