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
Related
I'm making a slideshow image and the direction of images is going left.
Now my problem is how can I make it an infinite loop? Once the last image appears, I want to show again the first image up to the last image again. How can I make this? Heres my code:
PictureBox[] clouds = new PictureBox[4];
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
speed = 3;
clouds[0] = pictureBox1;
clouds[1] = pictureBox2;
clouds[2] = pictureBox3;
clouds[3] = pictureBox4;
}
private void timer1_Tick(object sender, EventArgs e)
{
for (int x = 0; x < 4; x++)
{
clouds[x].Left -= 10;
if (clouds[x].Left == 0)
{
clouds[x].Left = +this.Width;
}
}
}
If I understand you right, you want
0, 1, 2, 3, 0, 1, 2, 3, 0, ...
sequence. It can be implemented as
// x = 0 - start with x = 0
// - no condition (infinte loop)
// x = (x + 1) %4 - modulo arithemetics: 0, 1, 2, 3, 0, 1, 2, 3, 0 ....
for (int x = 0; ; x = (x + 1) % 4) {
...
}
In general case for clouds[] array we can get rid of magic number 4
for (int x = 0; ; x = (x + 1) % array.Length) {
...
}
In Form1 I create in the constructor a new Bitmap:
public Form1()
{
InitializeComponent();
de.pb1 = pictureBox1;
de.bmpWithPoints = new Bitmap(pictureBox1.Width, pictureBox1.Height);
de.numberOfPoints = 100;
de.randomPointsColors = false;
de.Init();
}
In the class I check if the bitmap is null :
if (bmpWithPoints == null)
The bitmap is not null but is also not drawn with anything on it.
I check in the class if it's null I want to draw and set points on the bitmap.
if (bmpWithPoints == null)
{
for (int x = 0; x < bmpWithPoints.Width; x++)
{
for (int y = 0; y < bmpWithPoints.Height; y++)
{
bmpWithPoints.SetPixel(x, y, Color.Black);
}
}
Color c = Color.Red;
for (int x = 0; x < numberOfPoints; x++)
{
for (int y = 0; y < numberOfPoints; y++)
{
if (randomPointsColors == true)
{
c = Color.FromArgb(
r.Next(0, 256),
r.Next(0, 256),
r.Next(0, 256));
}
else
{
c = pointsColor;
}
bmpWithPoints.SetPixel(r.Next(0, bmpWithPoints.Width),
r.Next(0, bmpWithPoints.Height), c);
}
}
}
else
{
randomPointsColors = false;
}
Maybe the question should not be if the image is empty or null, I'm not sure how to call it. Maybe just a new image. But i want to check that if the new bitmap is (empty) nothing drawn on it then set the pixels(points).
You can create a method which checks image pixels. As an option, you can use LockBits method to get bitmap bytes into a byte array and use them:
bool IsEmpty(Bitmap image)
{
var data = image.LockBits(new Rectangle(0,0, image.Width,image.Height),
ImageLockMode.ReadOnly, image.PixelFormat);
var bytes = new byte[data.Height * data.Stride];
Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
image.UnlockBits(data);
return bytes.All(x => x == 0);
}
I'm having some trouble reading back pixel values from a Bitmap that I'm generating. I first generate a bitmap named maskBitmap in my class using this code:
void generateMaskBitmap()
{
if (inputBitmap != null)
{
Bitmap tempBitmap = new Bitmap(inputBitmap.Width, inputBitmap.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(tempBitmap))
{
Brush brush = Brushes.Black;
for (int y = 0; y < tempBitmap.Height; y += circleSpacing)
{
for (int x = 0; x < tempBitmap.Width; x += circleSpacing)
{
g.FillEllipse(brush, x, y, circleDiameter, circleDiameter);
}
}
g.Flush();
}
maskBitmap = (Bitmap)tempBitmap.Clone();
}
}
I then try to apply the mask to my original image using the following code:
void generateOutputBitmap()
{
if (inputBitmap != null && maskBitmap != null)
{
Bitmap tempBitmap = new Bitmap(inputBitmap.Width, inputBitmap.Height);
for (int y = 0; y < tempBitmap.Height; y++)
{
for (int x = 0; x < tempBitmap.Width; x++)
{
Color tempColor = maskBitmap.GetPixel(x, y);
if (tempColor == Color.Black)
{
tempBitmap.SetPixel(x, y, inputBitmap.GetPixel(x, y));
}
else
{
tempBitmap.SetPixel(x, y, Color.White);
}
}
}
outputBitmap = tempBitmap;
}
}
The mask bitmap is successfully generated and visible in a picture box, however the color values for every pixel when testing "tempColor" show empty (A = 0, R = 0, G = 0, B = 0). I am aware of the performance problems with getpixel/setpixel, but this is not an issue for this project. I'm also aware that "tempColor == Color.Black" is not a valid test, but that is just a place holder for my comparison code.
I am unable to reproduce your problem. I copy-and-pasted your code and made some modifications to make it work for me. I am able to confirm that tempColor is sometimes #FF000000.
I suspect you mixed up your bitmap references somewhere. Are you really sure you are never getting any value other than #00000000? Do your circleDiameter and circleSpacing have sensible values? And most importantly: are you absolutely sure that you are reading from the correct bitmap?
Here's my version of your code, which I know works:
using System;
using System.Drawing;
namespace Test
{
class Program
{
static void Main()
{
var bitmap = GenerateMaskBitmap(100, 100);
TestMaskBitmap(bitmap);
}
const int CircleDiameter = 10;
const int CircleSpacing = 10;
static Bitmap GenerateMaskBitmap(int width, int height)
{
Bitmap maskBitmap = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(maskBitmap))
{
Brush brush = Brushes.Black;
for (int y = 0; y < maskBitmap.Height; y += CircleSpacing)
{
for (int x = 0; x < maskBitmap.Width; x += CircleSpacing)
{
g.FillEllipse(brush, x, y, CircleDiameter, CircleDiameter);
}
}
g.Flush();
}
return maskBitmap;
}
static void TestMaskBitmap(Bitmap maskBitmap)
{
for (int y = 0; y < maskBitmap.Height; y++)
{
for (int x = 0; x < maskBitmap.Width; x++)
{
Color tempColor = maskBitmap.GetPixel(x, y);
if (tempColor.ToArgb() != 0)
throw new Exception("It works!");
}
}
}
}
}
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++)
{
...
}
}
This is supposed to calculate the histogram of an 8-bit grayscale image. With a 1024x770 test bitmap, CreateTime ends up at around 890ms. How can I make this go (way, way) faster?
EDIT: I should mention that this doesn't actually compute the histogram yet, it only gets the values out of the bitmap. So I really should have asked, what is the fastest way to retrieve all pixel values from an 8-bit grayscale image?
public class Histogram {
private static int[,] values;
public Histogram(Bitmap b) {
var sw = Stopwatch.StartNew();
values = new int[b.Width, b.Height];
for (int w = 0; w < b.Width; ++w) {
for (int h = 0; h < b.Height; ++h) {
values[w, h] = b.GetPixel(w, h).R;
}
}
sw.Stop();
CreateTime = (sw.ElapsedTicks /
(double)Stopwatch.Frequency) * 1000;
}
public double CreateTime { get; set; }
}
The basic histogram algorithm is something like:
int[] hist = new hist[256];
//at this point dont forget to initialize your vector with 0s.
for(int i = 0; i < height; ++i)
{
for(int j = 0 ; j < widthl ++j)
{
hist[ image[i,j] ]++;
}
}
The algorithm sums how many pixels with value 0 you have, how many with value=1 and so on.
The basic idea is to use the pixel value as the index to the position of the histogram where you will count.
I have one version of this algorithm written for C# using unmanaged code (which is fast) I dont know if is faster than your but feel free to take it and test, here is the code:
public void Histogram(double[] histogram, Rectangle roi)
{
BitmapData data = Util.SetImageToProcess(image, roi);
if (image.PixelFormat != PixelFormat.Format8bppIndexed)
return;
if (histogram.Length < Util.GrayLevels)
return;
histogram.Initialize();
int width = data.Width;
int height = data.Height;
int offset = data.Stride - width;
unsafe
{
byte* ptr = (byte*)data.Scan0;
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x, ++ptr)
histogram[ptr[0]]++;
ptr += offset;
}
}
image.UnlockBits(data);
}
static public BitmapData SetImageToProcess(Bitmap image, Rectangle roi)
{
if (image != null)
return image.LockBits(
roi,
ImageLockMode.ReadWrite,
image.PixelFormat);
return null;
}
I hope I could help you.
You'll want to use the Bitmap.LockBits method to access the pixel data. This is a good reference on the process. Essentially, you're going to need to use unsafe code to iterate over the bitmap data.
Here's a copy/pastable version of the function I've come up w/ based on on this thread.
The unsafe code expects the bitmap to be Format24bppRgb, and if it's not, it'll convert the bitmap to that format and operate on the cloned version.
Note that the call to image.Clone() will throw if you pass in a bitmap using an indexed pixel format, such as Format4bppIndexed.
Takes ~200ms to get a histogram from an image 9100x2048 on my dev machine.
private long[] GetHistogram(Bitmap image)
{
var histogram = new long[256];
bool imageWasCloned = false;
if (image.PixelFormat != PixelFormat.Format24bppRgb)
{
//the unsafe code expects Format24bppRgb, so convert the image...
image = image.Clone(new Rectangle(0, 0, image.Width, image.Height), PixelFormat.Format24bppRgb);
imageWasCloned = true;
}
BitmapData bmd = null;
try
{
bmd = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly,
PixelFormat.Format24bppRgb);
const int pixelSize = 3; //pixels are 3 bytes each w/ Format24bppRgb
//For info on locking the bitmap bits and finding the
//pixels using unsafe code, see http://www.bobpowell.net/lockingbits.htm
int height = bmd.Height;
int width = bmd.Width;
int rowPadding = bmd.Stride - (width * pixelSize);
unsafe
{
byte* pixelPtr = (byte*)bmd.Scan0;//starts on the first row
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
histogram[(pixelPtr[0] + pixelPtr[1] + pixelPtr[2]) / 3]++;
pixelPtr += pixelSize;//advance to next pixel in the row
}
pixelPtr += rowPadding;//advance ptr to the next pixel row by skipping the padding # the end of each row.
}
}
}
finally
{
if (bmd != null)
image.UnlockBits(bmd);
if (imageWasCloned)
image.Dispose();
}
return histogram;
}