I'm trying to convert an array of bytes to Bitmap image using following code:
using (Graphics g = Graphics.FromImage(tilePage))
g.Clear(Color.Black);
for (int i = 0; i + offset < ROM.Length && i < tilePage.Height; i++)
{
int I = i * tilePage.Width + offset; //+Offset;
for (int j = 0; j < tilePage.Width; j++)
{
if (I + j >= ROM.Length)
goto finishRender;
tilePage.SetPixel(j, i, RPallete.getColor(ROM[I + j]));
}
}
finishRender:
picTileView.Image = tilePage;
but it renders too slow (2-3 seconds for 256*512 resolution). How to make it faster for example as in TileLayer?
P.S.: Sorry for bad English...
You can use pointer. I used recently this code. You can improve as you wish
add picturebox that named pctResim
Check allow unsafe on project properties
Try this :
byte siyah=0;
int dizisayisi;
IntPtr baslangic;
byte[] rgbdeger;
Bitmap resim;
Rectangle rct;
BitmapData bmData;
resim = new Bitmap(#"C:\YazilimGrubu.jpg");
rct = new Rectangle(0, 0, resim.Width, resim.Height);
bmData = resim.LockBits(rct, ImageLockMode.ReadWrite, resim.PixelFormat);
baslangic = bmData.Scan0;
dizisayisi = bmData.Stride * resim.Height;
rgbdeger = new byte[dizisayisi];
Marshal.Copy(baslangic, rgbdeger, 0, dizisayisi);
for (int i = 2; i < rgbdeger.Length; i += 3)
{
siyah = (Byte)Math.Abs((Byte.Parse(rgbdeger[i - 2].ToString()) + Byte.Parse(rgbdeger[i - 1].ToString()) + Byte.Parse(rgbdeger[i].ToString())) / 3);
rgbdeger[i - 2] = siyah;
rgbdeger[i - 1] = siyah;
rgbdeger[i] = siyah;
}
Marshal.Copy(rgbdeger, 0, baslangic, dizisayisi);
resim.UnlockBits(bmData);
pctResim.Image = resim;
Related
Using the Sobel edge detector code below I find that the output bitmap has a diagonal line of zero values superimposed over detected edges if the input bitmap has a width not divisible by 4. The red square marked in the output bitmap at co-ords (80,80) is broken up and incorrectly placed in this case. Why is this and how can I make the code work with any bitmap width?
private Bitmap SobelEdgeDetect2(Bitmap original, byte Threshold = 128)
{
// https://stackoverflow.com/questions/16747257/edge-detection-with-lockbits-c-sharp
int width = original.Width;
int height = original.Height;
int BitsPerPixel = Image.GetPixelFormatSize(original.PixelFormat);
int OneColorBits = BitsPerPixel / 8;
BitmapData bmpData = original.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, original.PixelFormat);
int position;
int[,] gx = new int[,] { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } };
int[,] gy = new int[,] { { 1, 2, 1 }, { 0, 0, 0 }, { -1, -2, -1 } };
Bitmap dstBmp = new Bitmap(width, height, original.PixelFormat);
BitmapData dstData = dstBmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, dstBmp.PixelFormat);
int byteCount = dstData.Stride * dstBmp.Height;
byte[] input = new byte[byteCount];
byte[] processed = new byte[byteCount];
IntPtr ptr = bmpData.Scan0;
IntPtr dst = dstData.Scan0;
Marshal.Copy(ptr, input, 0, input.Length);
Marshal.Copy(dst,processed, 0, input.Length);
int BlackPoints = 0;
int WhitePoints = 0;
for (int i = 1; i < height - 1; i++) // y
{
for (int j = 1; j < width - 1; j++) // x
{
int NewX = 0, NewY = 0;
for (int ii = 0; ii < 3; ii++)
{
for (int jj = 0; jj < 3; jj++)
{
int I = i + ii - 1;
int J = j + jj - 1;
byte Current = input[(I * (width) + J) * OneColorBits];
NewX += gx[ii, jj] * Current;
NewY += gy[ii, jj] * Current;
}
}
position = (i * (width) + j) * OneColorBits;
if (NewX * NewX + NewY * NewY > Threshold * Threshold)
{
processed[position] = 255;
processed[position + 1] = 255;
processed[position + 2] = 255;
WhitePoints++;
}
else
{
processed[position] = 0;
processed[position + 1] = 0;
processed[position + 2] = 0;
BlackPoints++;
}
if (j >= 78 && j <= 82 && i >= 78 && i <= 82)
{
processed[position] = 0;
processed[position + 1] = 0;
processed[position + 2] = 255;
}
}
}
Marshal.Copy(processed, 0, dst, input.Length);
dstBmp.UnlockBits(dstData);
return dstBmp;
}
For a 201 pixel wide bitmap, dstData.Stride was 604. For a 200 pixel wide bitmap dstData.Stride was 612, which explains why width had to be divisible by 4 for my code.
Replacing
position = (i * (width) + j) * OneColorBits;
by
position = i * dstData.Stride + j * OneColorBits;
and
byte Current = input[(I * (width) + J) * OneColorBits];
by
byte Current = input[I * dstData.Stride + J * OneColorBits];
fixed the problem.
I am trying to split an image of hand written digits into separate ones.
Consider I have this image:
I did a simple logic that could work, but it will and it did encounter a problem:
private static void SplitImages()
{
//We're going to use this code once.. to split our own images into seperate images.. can we do this somehow?
Bitmap testSplitImage = (Bitmap)Bitmap.FromFile("TestSplitImage.jpg");
int[][] imagePixels = new int[testSplitImage.Width][];
for(int i=0;i<imagePixels.Length;i++)
{
imagePixels[i] = new int[testSplitImage.Height];
}
for(int i=0;i<imagePixels.Length;i++)
{
for(int j=0;j<imagePixels[i].Length;j++)
{
Color c = testSplitImage.GetPixel(i, j);
imagePixels[i][j] = (c.R + c.G + c.B) / 3;
}
}
//let's start by getting the first height vector... and count how many of them is white..dunno..
int startColNumber = 0;
int endColNumber = 0;
bool isStart = false;
int imageNumber = 1;
for(int i=0;i<imagePixels.Length;i++)
{
int whiteNumbers = 0;
for(int j=0;j<imagePixels[i].Length;j++)
{
if (imagePixels[i][j] > 200)
{
//consider it white or not really relevant
whiteNumbers++;
}
}
if (whiteNumbers > testSplitImage.Height*95.0/100.0)
{
//let's consider that if a height vector has more than 95% white pixels.. it means that we can start checking for an image
//now if we started checking for the image.. we need to stop
if (isStart)
{
//consider the end of image.. so the end column should be here or we make it +1 at least
endColNumber = i + 1;
isStart = false;
}
}
else
{
if (!isStart)
{
isStart = true; //we will start checking for the image one row before that maybe?
startColNumber = i == 0 ? i : i - 1;
}
}
if (endColNumber > 0)
{
//we got a start and an end.. let's create a new image out of those pixels..hopefully this will work
Bitmap splittedImage = new Bitmap(endColNumber - startColNumber + 1, testSplitImage.Height);
int col = 0;
for(int k=startColNumber;k<=endColNumber;k++)
{
for (int l=0;l<testSplitImage.Height;l++)
{
int c = imagePixels[k][l];
splittedImage.SetPixel(col, l, Color.FromArgb(c, c, c));
}
col++;
}
splittedImage.Save($"Image{imageNumber++}.jpg");
endColNumber = 0;
}
whiteNumbers = 0;
}
}
I did get good results:
I did also get the three zeros:
However, I got this as one image also:
This is one sample of an image that needs to be split (out of 4,000 images mainly), and it's one of the best and easiest one. I am wondering if there's a way to improve my logic, or I should drop this way and find another?
This code only works with monochrome (2 color, black and white) images.
public static class Processor
{
public static byte[] ToArray(this Bitmap bmp) // bitmap to byte array using lockbits
{
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData data = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
IntPtr ptr = data.Scan0;
int numBytes = data.Stride * bmp.Height;
byte[] bytes = new byte[numBytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, bytes, 0, numBytes);
bmp.UnlockBits(data);
return bytes;
}
public static int GetPixel(this byte[] array, int bpr, int x, int y) //find out if the given pixel is 0 or 1
{
int num = y * bpr + x / 8;
return (array[num] >> 7- x%8) & 1;
}
public static List<Point> getDrawingPoints(this Point start, byte[] array, int width, int height) // get one 0 point (black point) and find all adjacent black points by traveling neighbors
{
List<Point> points = new List<Point>();
points.Add(start);
int BytePerRow = array.Length / bmp.Height;
int counter = 0;
do
{
for (int i = Math.Max(0, points[counter].X - 1); i <= Math.Min(width - 1, points[counter].X + 1); i++)
for (int j = Math.Max(0, points[counter].Y - 1); j <= Math.Min(height - 1, points[counter].Y + 1); j++)
if (array.GetPixel(BytePerRow, i, j) == 0 && !points.Any(p => p.X == i && p.Y == j))
points.Add(new Point(i, j));
counter++;
} while (counter < points.Count);
return points;
}
public static Bitmap ToBitmap(this List<Point> points) // convert points to bitmap
{
int startX = points.OrderBy(p => p.X).First().X,
endX = points.OrderByDescending(p => p.X).First().X,
startY = points.OrderBy(p => p.Y).First().Y,
endY = points.OrderByDescending(p => p.Y).First().Y;
Bitmap bmp = new Bitmap(endX - startX + 1, endY - startY + 1);
Graphics g = Graphics.FromImage(bmp);
g.FillRectangle(new SolidBrush(Color.White), new Rectangle(0, 0, endX - startX - 1, endY - startY - 1));
for (int i = startY; i <= endY; i++)
for (int j = startX; j <= endX; j++)
if (points.Any(p => p.X == j && p.Y == i)) bmp.SetPixel(j - startX, i - startY, Color.Black);
return bmp;
}
}
And use it like this to get all numbers inside the main image:
List<Point> processed = new List<Point>();
Bitmap bmp = ((Bitmap)Bitmap.FromFile(SourceBitmapPath));
byte[] array = bmp.ToArray();
int BytePerRow = array.Length / bmp.Height;
int imgIndex = 1;
for (int i = 0; i < bmp.Width; i++)
for (int j = 0; j < bmp.Height; j++)
{
if (array.GetPixel(BytePerRow, i, j) == 0 && !processed.Any(p => p.X == i && p.Y == j))
{
List<Point> points = new Point(i, j).getDrawingPoints(array, bmp.Width, bmp.Height);
processed.AddRange(points);
Bitmap result = points.ToBitmap();
result.Save($"{imgIndex++}.bmp");
}
}
I'm using paint and Save As monochrome bmp format to generate the source image.
I also tested it with this Image:
that result in the following three images:
I am trying to perform a basic number-only OCR on an image by comparing it to bitmaps of the numbers 0 - 9, using the code below. I have tried to follow the code in the answer to this question, but it is not returning the correct results. There are 2 main issues that I am facing:
1: If the program determines that the number 0 is present at any given point, then it also determines that 1, 2, 3, ... , and 9 are present at that location, which is obviously not true.
2: The locations that it finds numbers in ... most of the locations are blank (white) spaces.
I'll be the first to admit that using the lockbits method is new to me, as I usually use the getPixel() method of comparing, but it was far too slow for this project, so I may be making a rookie mistake or 2.
Thanks for the help!!!
P.S. The image to OCR is RTA, and I believe everything else is self-explanatory.
void newOCR()
{
Rectangle rect = new Rectangle(0, 0, 8, 9);
Rectangle numRect = new Rectangle(0, 0, 8, 9);
for (int i = 0; i < RTA.Width - 8; i++)
{
for (int j = 0; j < RTA.Height - 9; j++)
{
rect.Location = new Point(i, j);
for (int n = 0; n < numbers.Length; n++)
{
System.Drawing.Imaging.BitmapData data = RTA.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, RTA.PixelFormat);
System.Drawing.Imaging.BitmapData numData = numbers[n].LockBits(numRect, System.Drawing.Imaging.ImageLockMode.ReadOnly, numbers[n].PixelFormat);
unsafe
{
byte* ptr = (byte*)data.Scan0.ToPointer();
byte* numPtr = (byte*)data.Scan0.ToPointer();
int width = rect.Width * Image.GetPixelFormatSize(data.PixelFormat) / 8;
for(int y = 0; y < rect.Height; y++)
{
bool outBreak = false;
for(int x = 0; x < width; x++)
{
if(*ptr != *numPtr)
{
outBreak = true;
break;
}
else if(y == rect.Height - 1 && x == width - 1)
{
timeDict.Add(new Point(i, j), n);
}
ptr++;
numPtr++;
}
if(outBreak)
{
break;
}
ptr += data.Stride - width;
numPtr += numData.Stride - width;
}
RTA.UnlockBits(data);
numbers[n].UnlockBits(numData);
}
}
}
}
}
There is a (probably copy/paste) mistake in the following line
byte* numPtr = (byte*)data.Scan0.ToPointer();
which is causing comparing the bitmap to itself. It should be
byte* numPtr = (byte*)numData.Scan0.ToPointer();
I have some code which has the error "AccessViolationException was unhandled by user code: Attempted to read or write protected memory..."
A trimmed down version of the offending function is as follows:
protected override void OnPaint(PaintEventArgs pe)
{
if ((updatingFastBackground) || (Calculating)) return; //ADDED FOR DEBUGGING, SEE BELOW
BitmapData canvasData = Canvas.LockBits(new Rectangle(Point.Empty, Canvas.Size), ImageLockMode.WriteOnly, FastPixelFormat);
BitmapData fbgData = fastBackground.LockBits(new Rectangle(Point.Empty, fastBackground.Size), ImageLockMode.ReadOnly, FastPixelFormat);
try
{
unsafe
{
byte* canvasDataScan0 = (byte*)canvasData.Scan0.ToPointer();
byte* fbgDataScan0 = (byte*)fbgData.Scan0.ToPointer();
Rectangle spriteBounds = new Rectangle(Point.Empty, ButtonImages.ImageSize);
for (int i = 0; i < ButtonImages.Images.Count; i++)
{
// Button offset location
Point l = new Point(
(int)((i % columnCount) * hStep + myIVM.Location.X),
(int)((i / columnCount) * vStep + myIVM.Location.Y));
// Paint at current location?
if (buttonPaintBounds.Contains(l))
{
BitmapData spriteData = buttonBitmaps[i].LockBits(spriteBounds, ImageLockMode.ReadOnly, FastPixelFormat);
try
{
int spriteLeft = Math.Max(l.X, 0);
int spriteRight = Math.Min(l.X + ButtonImages.ImageSize.Width, canvasData.Width);
int spriteTop = Math.Max(l.Y, 0);
int spriteBottom = Math.Min(l.Y + ButtonImages.ImageSize.Height, canvasData.Height);
int spriteWidth = spriteRight - spriteLeft;
int spriteHeight = spriteBottom - spriteTop;
byte* canvasRowLeft = canvasDataScan0 + (spriteTop * canvasData.Stride) + spriteLeft * 4;
byte* spriteRowLeft =
(byte*)spriteData.Scan0.ToPointer() +
Math.Max((spriteTop - l.Y), 0) * spriteData.Stride +
Math.Max((spriteLeft - l.X), 0) * 4;
for (int y = 0; y < spriteHeight; y++)
{
canvasRowLeft += canvasData.Stride;
spriteRowLeft += spriteData.Stride;
Byte* canvasWalk = (Byte*)canvasRowLeft;
Byte* spriteWalk = (Byte*)spriteRowLeft;
for (int x = 0; x < spriteWidth; x++)
{
if (spriteWalk[3] != 255)
{
canvasWalk[0] = (byte)(canvasWalk[0] * spriteWalk[3] / 255 + spriteWalk[0]);
canvasWalk[1] = (byte)(canvasWalk[1] * spriteWalk[3] / 255 + spriteWalk[1]);
canvasWalk[2] = (byte)(canvasWalk[2] * spriteWalk[3] / 255 + spriteWalk[2]);
}
canvasWalk += 4;
spriteWalk += 4;
}
}
thesePoints.Add(l);
}
finally
{
buttonBitmaps[i].UnlockBits(spriteData);
}
}
The error occurs on the line:
canvasWalk[0] = (byte)(canvasWalk[0] * spriteWalk[3] / 255 + spriteWalk[0]);
and even when replaced with:
canvasWalk[0] = 0;
The iteration variables y and x have different values each time it crashes so this leads me to believe an external function is modifying the Canvas bitmap.
IF this is in fact my problem, is there a way to prevent fastBackground and Canvas from being externally modified? I thought LockBits was supposed to do that that...
If that's not enough to answer, here's some more I've tried:
I added the line
if ((updatingFastBackground) || (Calculating)) return;
to exit OnPaint if fastBackground Canvas or dimensions are being modified by other functions.
I could use a mutex to prevent the functions that modify the bitmaps fastBackground and Canvas from being run at the same time as paint (as I think they must be) but I'd rather block them another way as Canvas is public and I don't want to require passing a mutex out of the class.
per #usr 's suggestion, this further trimmed down version doesn't fail... Must have been a PTD error. (programmer too dumb) i.e. arithmetic error
protected override void OnPaint(PaintEventArgs pe)
{
if ((updatingFastBackground) || (Calculating)) return; //ADDED FOR DEBUGGING, SEE BELOW
BitmapData canvasData = Canvas.LockBits(new Rectangle(Point.Empty, Canvas.Size), ImageLockMode.WriteOnly, FastPixelFormat);
BitmapData fbgData = fastBackground.LockBits(new Rectangle(Point.Empty, fastBackground.Size), ImageLockMode.ReadOnly, FastPixelFormat);
try
{
unsafe
{
byte* canvasDataScan0 = (byte*)canvasData.Scan0.ToPointer();
byte* fbgDataScan0 = (byte*)fbgData.Scan0.ToPointer();
Rectangle spriteBounds = new Rectangle(Point.Empty, ButtonImages.ImageSize);
for (int i = 0; i < ButtonImages.Images.Count; i++)
{
// Button offset location
Point l = new Point(
(int)((i % columnCount) * hStep + myIVM.Location.X),
(int)((i / columnCount) * vStep + myIVM.Location.Y));
// Paint at current location?
if (buttonPaintBounds.Contains(l))
{
BitmapData spriteData = buttonBitmaps[i].LockBits(spriteBounds, ImageLockMode.ReadOnly, FastPixelFormat);
try
{
byte* canvasRowLeft = canvasDataScan0;
byte* spriteRowLeft = (byte*)spriteData.Scan0.ToPointer();
for (int y = 0; y < 145; y++)
{
canvasRowLeft += canvasData.Stride;
spriteRowLeft += spriteData.Stride;
Byte* canvasWalk = (Byte*)canvasRowLeft;
Byte* spriteWalk = (Byte*)spriteRowLeft;
for (int x = 0; x < 145; x++)
{
if (spriteWalk[3] != 255)
{
canvasWalk[0] = 0;
canvasWalk[1] = 0;
canvasWalk[2] = 0;
}
canvasWalk += 4;
spriteWalk += 4;
}
}
thesePoints.Add(l);
}
finally
{
buttonBitmaps[i].UnlockBits(spriteData);
}
}
Moving my comment into an answer because it helped solve the problem:
Fixed is not required for the buffer returned by LockBits because it is unmanaged memory. Your pointer arithmetic is wrong. Find the bug. Create a simple repro to help find the bug.
Graphics g;
using (var bmp = new Bitmap(_frame, _height, PixelFormat.Format24bppRgb))
{
var data = bmp.LockBits(new Rectangle(0, 0, _frame, _height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
var bmpWidth = data.Stride;
var bytes = bmpWidth * _height;
var rgb = new byte[bytes];
var ptr = data.Scan0;
Marshal.Copy(ptr, rgb, 0, bytes);
for (var i = 0; i < _frame; i++)
{
var i3 = (i << 1) + i;
for (var j = 0; j < _height; j++)
{
var ij = j * bmpWidth + i3;
var val = (byte)(_values[i, j]);
rgb[ij] = val;
rgb[ij + 1] = val;
rgb[ij + 2] = val;
}
}
Marshal.Copy(rgb, 0, ptr, bytes);
bmp.UnlockBits(data);
g = _box.CreateGraphics();
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.DrawImage(bmp, 0, 0, _box.Width, _box.Height);
}
g.Dispose();
I use this code to convert an array of RGB values (grayscale) in the PictureBox, but it's slow. Please tell me my mistakes.
At the moment, an array of 441 000 items handled for 35 ms.
I need to handle an array of 4 million for the same time.
You can skip the first Array.Copy where you copy the data from the image to the array, as you will be overwriting all the data in the array anyway.
That will shave off something like 25% of time, but if you want it faster you will have to use an unsafe code block so that you can use pointers. That way you can get around the range checking when you access arrays, and you can write the data directly into the image data instead of copying it.
I totally agree with Guffa's answer. Using an unsafe code block will speed up things.
To further improve performance, you could execute your for loop in parallel by using the Parallel class in the .Net framework. For large bitmaps this improves performance.
Here is a small code sample:
using (Bitmap bmp = (Bitmap)Image.FromFile(#"mybitmap.bmp"))
{
int width = bmp.Width;
int height = bmp.Height;
BitmapData bd = bmp.LockBits(new Rectangle(0, 0, width, height),
System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
byte* s0 = (byte*)bd.Scan0.ToPointer();
int stride = bd.Stride;
Parallel.For(0, height, (y1) =>
{
int posY = y1*stride;
byte* cpp = s0 + posY;
for (int x = 0; x < width; x++)
{
// Set your pixel values here.
cpp[0] = 255;
cpp[1] = 255;
cpp[2] = 255;
cpp += 3;
}
});
bmp.UnlockBits(bd);
}
To keep the example simple I've set the pixel values to a constant value. Note, to compile the example above you have to allow unsafe code.
Hope, this helps.
In addition to Guffa's excellent advice, I would suggest that you profile your code to see where it's taking the time. Be sure that when you're timing this, you are running in release mode without the debugger attached.
I wouldn't be surprised if the call to DrawImage is taking up most of the time. You're scaling the image there, which can be pretty expensive. How large is the box that you're drawing the image to?
Finally, although this won't affect performance, you should change your code to read:
using (Graphics g = _box.CreateGraphics())
{
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.DrawImage(bmp, 0, 0, _box.Width, _box.Height);
}
And get rid of the first and last lines in your example.
Try this using unsafe code:
byte* rp0;
int* vp0;
fixed (byte* rp1 = rgb)
{
rp0 = rp1;
fixed (int* vp1 = _values)
{
vp0 = vp1;
Parallel.For(0, _width, (i) =>
{
var val = (byte)vp0[i];
rp0[i] = val;
rp0[i + 1] = val;
rp0[i + 2] = val;
});
}
}
Runs very fast for me
My understanding is that multidimentional (square) arrays are pretty slow in .Net. You might try changing your _values array to be a single dimension array instead.
Here is one reference, there are many more if you search:
http://odetocode.com/articles/253.aspx
Array perf example.
using System;
using System.Diagnostics;
class Program
{
static void Main(string[] args)
{
int w = 1000;
int h = 1000;
int c = 1000;
TestL(w, h);
TestM(w, h);
var swl = Stopwatch.StartNew();
for (int i = 0; i < c; i++)
{
TestL(w, h);
}
swl.Stop();
var swm = Stopwatch.StartNew();
for (int i = 0; i < c; i++)
{
TestM(w, h);
}
swm.Stop();
Console.WriteLine(swl.Elapsed);
Console.WriteLine(swm.Elapsed);
Console.ReadLine();
}
static void TestL(int w, int h)
{
byte[] b = new byte[w * h];
int q = 0;
for (int x = 0; x < w; x++)
for (int y = 0; y < h; y++)
b[q++] = 1;
}
static void TestM(int w, int h)
{
byte[,] b = new byte[w, h];
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
b[y, x] = 1;
}
}