I have a code written in c# that can recognize digits written on
an image (bitmap).
please, can you help to convert this code to c++ using the greate wxWidgets libraries.
Thanks.
Code: Select all
class Number
{
public static string GetText(Bitmap map)
{
Number[] defaultNumbers = BuildDefaultImage();
System.Collections.ArrayList numbers = new System.Collections.ArrayList();
//find numbers
if (!ProcessImage(map,numbers))
return "invalid image format";
map.Dispose();
string output = "";
//for each number found...
for (int i = 0; i < numbers.Count; i++)
{
int max = 0;
int index = 0;
for (int n = 0; n < 10; n++)
{
//get the difference between this number and each reference number (brute force)
int dif = ((Number)numbers[i]).Compare(defaultNumbers[n]);
if (max == 0 || dif < max) // get the lowest difference
{
max = dif;
index = n;
}
}
output += index.ToString();
}
return output;
}
static Number[] defaults;
readonly byte[] data;
const int SampleX = 8, SampleY = 16;
static Number[] BuildDefaultImage()
{
if (defaults!=null)
return defaults;
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(140,30);
System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmp);
System.Drawing.Font f = new System.Drawing.Font("Arial",15,System.Drawing.FontStyle.Regular);
System.Drawing.Brush b = new System.Drawing.SolidBrush(System.Drawing.Color.Black);
g.Clear(System.Drawing.Color.White);
g.DrawString("0123456789",f,b,1,1);
b.Dispose();
f.Dispose();
g.Dispose();
bmp.Save("test.bmp");
System.Collections.ArrayList numbers =new System.Collections.ArrayList();
//get all the numbers
if (!ProcessImage(bmp,numbers))
return null;
bmp.Dispose();
defaults = new Number[numbers.Count];
for (int n=0; n<defaults.Length; n++)
defaults[n] = (Number)numbers[n];
return defaults;
}
/*
*
* This of course, is the complex bit.
*
*/
static bool ProcessImage(System.Drawing.Bitmap image, System.Collections.ArrayList numbers)
{
//number of bytes per pixel
int bytePixelStride = System.Drawing.Image.GetPixelFormatSize(image.PixelFormat) / 8;
if (bytePixelStride == 0)
return false;
//this makes the rest of the code more efficient (sequential access, etc), ignore
image.RotateFlip(System.Drawing.RotateFlipType.Rotate90FlipNone);
//copy the bitmap data out
System.Drawing.Imaging.BitmapData bd = image.LockBits(new System.Drawing.Rectangle(0, 0, image.Width, image.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, image.PixelFormat);
byte[] imageData = new byte[bd.Stride * image.Height];
System.Runtime.InteropServices.Marshal.Copy(bd.Scan0, imageData, 0, imageData.Length);
image.UnlockBits(bd);
int stride = bd.Stride;
//values used during next loop
int start = -1, end = 0, startHeight = image.Width * bytePixelStride;
//this loop runs through the rows of pixels in the image, looking for rows with non white pixels (ie, rows with numbers :)
//the first time a row is hit, the row number is stored. When a blank row is then hit, the end value is stored.
//therefore, this loop generates pairs of start/end values, representing where non-white rows start and end.
//Ie, where numbers start and end. As a speed boost, the minimum height bottom edge of the number is also stored in startHeight.
for (int x = 0; x < image.Height; x++)
{
int index = x * stride;
int height = image.Width * bytePixelStride;
bool emptyRow = true;
//pixels in the row..
for (int y = index; y < index + image.Width * bytePixelStride; y += bytePixelStride)
{
if (imageData[y] < 128)
{
//this is a row with a number on it,
if (end != start)
{
//if end and start are different, the previous row was white.
start = x;
end = x;
}
//work out start height of number
if (y - index < height)
height = y - index;
emptyRow = false;
break;
}
}
if (!emptyRow)
{
if (height < startHeight)
startHeight = height; // min start height of number
}
if (emptyRow && end == start)
{
//make a new number object
end = x;
startHeight /= bytePixelStride;
Number number = new Number(start, end, imageData, startHeight, image.Width, stride, bytePixelStride);
numbers.Add(number);
//reset
startHeight = image.Width * bytePixelStride;
}
}
image.Dispose();
return true;
}
//saved values from values.dat
public Number(byte[] data, int index)
{
this.data = new byte[SampleX * SampleY];
int start = SampleX * SampleY * index;
for (int i = 0; i < this.data.Length; i++)
this.data[i] = data[start + i];
}
//build the number
public Number(int x, int x_end, byte[] imageData, int startHeight, int imageWidth, int stride, int byteCount)
{
this.data = null;
int endHeight = 0;
//now, the start x positon, width and start y positon are known.
//All that is needed is to find the height of the number
for (int row = x; row < x_end; row++)
{
int rowEndHeight = 0;
int rowStart = row * stride + startHeight * byteCount;
int rowEnd = row * stride + imageWidth * byteCount;
//loop along the rows
for (int i = rowStart; i < rowEnd; i += byteCount)
{
//if pixel is not white, increase this rows Height
if (imageData[i] < 128)
rowEndHeight = i;
}
rowEndHeight -= rowStart;
rowEndHeight /= byteCount;
if (endHeight < rowEndHeight)
endHeight = rowEndHeight;
}
int width = x_end - x;
int y = startHeight;
int height = endHeight;
//now have the bounds of the number, x,y,width,height
//proceed to resize it to a 16x8 image
data = new byte[SampleX * SampleY];
//this saves calculation inside the loop
int[] scalersY = new int[SampleY];
//nearest neighbour sampling indices on the y axis
for (int i = 0; i < SampleY; i++)
scalersY[i] = (y + (i * height) / (SampleY - 1)) * byteCount;
//cheap nearest neighbour resampling
int index = 0;
for (int rx = 0; rx < SampleX; rx++)
{
//nearest neighbour sample on xaxis
int image_x = (x + (rx * width) / (SampleX - 1)) * stride;
for (int ry = 0; ry < SampleY; ry++)
{
int image_y = scalersY[ry];
data[index] = imageData[image_x + image_y];
index++;
}
}
//data is now set.
}
//compares this to another number
public int Compare(Number l2)
{
int compare = 0;
for (int i = 0; i < data.Length; i++)
{
//compare pixel difference
int dif = (int)data[i] - (int)l2.data[i];
//take absolute value, and add to total difference
if (dif < 0)
compare -= dif;
else
compare += dif;
}
return compare;
}
}
}