396 lines
12 KiB
C#
396 lines
12 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Text;
|
|||
|
|
using System.Drawing;
|
|||
|
|
using System.Drawing.Imaging;
|
|||
|
|
using System.Windows.Forms;
|
|||
|
|
namespace TraceOutline
|
|||
|
|
{
|
|||
|
|
class CTraceOuline
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
|
|||
|
|
public int color_threshold = 200;
|
|||
|
|
public bool use_red = true;
|
|||
|
|
public bool use_green = true;
|
|||
|
|
public bool use_blue = true;
|
|||
|
|
|
|||
|
|
|
|||
|
|
//for argb color pixel format
|
|||
|
|
private int CoordsToIndex(int x, int y, int stride)
|
|||
|
|
{
|
|||
|
|
return (stride * y) + (x * 4);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public int GetGrayScaleColor(Color c)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
int numcolorplane = 3;
|
|||
|
|
|
|||
|
|
numcolorplane = (!use_blue) ? numcolorplane - 1 : numcolorplane;
|
|||
|
|
numcolorplane = (!use_green) ? numcolorplane - 1 : numcolorplane;
|
|||
|
|
numcolorplane = (!use_red) ? numcolorplane - 1 : numcolorplane;
|
|||
|
|
|
|||
|
|
if (numcolorplane == 0)
|
|||
|
|
return (((int)c.B + (int)c.G + (int)c.R) / 3);
|
|||
|
|
|
|||
|
|
int accvalue = 0;
|
|||
|
|
accvalue = use_blue ? accvalue + (int)c.B : accvalue;
|
|||
|
|
accvalue = use_green ? accvalue + (int)c.G : accvalue;
|
|||
|
|
accvalue = use_red ? accvalue + (int)c.R : accvalue;
|
|||
|
|
|
|||
|
|
|
|||
|
|
return (int)(accvalue / numcolorplane);
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private int GetMonoColor(Color c)
|
|||
|
|
{
|
|||
|
|
int i = GetGrayScaleColor(c);
|
|||
|
|
if (i < color_threshold)
|
|||
|
|
return 0;
|
|||
|
|
else
|
|||
|
|
return 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
public Point[] StringOutline2Polygon(string outline)
|
|||
|
|
{
|
|||
|
|
string[] s = outline.Split(';');
|
|||
|
|
|
|||
|
|
if (s.Length < 5)
|
|||
|
|
return null;
|
|||
|
|
|
|||
|
|
Point[] p = new Point[s.Length - 1];
|
|||
|
|
|
|||
|
|
string[] s1 = s[0].Split(',');
|
|||
|
|
for (int i = 0; i < s.Length - 1; i++)
|
|||
|
|
{
|
|||
|
|
s1 = s[i].Split(',');
|
|||
|
|
p[i].X = int.Parse(s1[0]);
|
|||
|
|
p[i].Y = int.Parse(s1[1]);
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
return p;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
public string TraceOutlineN(Bitmap bm, int x0, int y0, int probe_width, Color fg, Color bg, bool bauto_threshold, int n)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
string s = "";
|
|||
|
|
int x = 0, y = y0;
|
|||
|
|
int x1 = 0, y1 = 0;
|
|||
|
|
Color c1, c2;
|
|||
|
|
int start_direction = 0, current_direction = 0;
|
|||
|
|
|
|||
|
|
int gc1 = 0;
|
|||
|
|
int gc2 = 0;
|
|||
|
|
bool hitborder = false;
|
|||
|
|
bool hitstart = false;
|
|||
|
|
int max_width = bm.Width, max_height = bm.Height;
|
|||
|
|
|
|||
|
|
//direct bit manipulation
|
|||
|
|
Rectangle rect = new Rectangle(0, 0, bm.Width, bm.Height);
|
|||
|
|
BitmapData bmpData =
|
|||
|
|
bm.LockBits(rect, ImageLockMode.ReadOnly,
|
|||
|
|
bm.PixelFormat);
|
|||
|
|
IntPtr ptr = bmpData.Scan0;
|
|||
|
|
int bytes = bm.Width * bm.Height * 4;
|
|||
|
|
byte[] rgbValues = new byte[bytes];
|
|||
|
|
|
|||
|
|
// Copy the RGB values into the array.
|
|||
|
|
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
|
|||
|
|
bm.UnlockBits(bmpData);
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
if (bauto_threshold)
|
|||
|
|
{
|
|||
|
|
//get max pix value diff
|
|||
|
|
int maxpixdiff = 0;
|
|||
|
|
int maxpixvalue = 0;
|
|||
|
|
|
|||
|
|
for (int i = 0; i < (probe_width * 2); i++)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
if (i % 2 == 1)
|
|||
|
|
x = x0 + i / 2;
|
|||
|
|
else
|
|||
|
|
x = x0 - i / 2;
|
|||
|
|
|
|||
|
|
|
|||
|
|
if (x < 0) continue;
|
|||
|
|
if (x >= max_width) continue;
|
|||
|
|
|
|||
|
|
int index = CoordsToIndex(x, y0, bmpData.Stride);
|
|||
|
|
if (index < 0 || index > (bytes - 1)) break;
|
|||
|
|
c1 = Color.FromArgb(rgbValues[index + 2], rgbValues[index + 1], rgbValues[index]);
|
|||
|
|
|
|||
|
|
gc1 = GetGrayScaleColor(c1);
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
index = CoordsToIndex(x + 1, y0, bmpData.Stride);
|
|||
|
|
if (index < 0 || index > (bytes - 1)) break;
|
|||
|
|
c2 = Color.FromArgb(rgbValues[index + 2], rgbValues[index + 1], rgbValues[index]);
|
|||
|
|
|
|||
|
|
gc2 = GetGrayScaleColor(c2);
|
|||
|
|
|
|||
|
|
if (maxpixdiff < Math.Abs(gc1 - gc2))
|
|||
|
|
maxpixdiff = Math.Abs(gc1 - gc2);
|
|||
|
|
|
|||
|
|
if (gc1 > maxpixvalue)
|
|||
|
|
maxpixvalue = gc1;
|
|||
|
|
if (gc2 > maxpixvalue)
|
|||
|
|
maxpixvalue = gc2;
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
catch (Exception)
|
|||
|
|
{
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (maxpixdiff > 0)
|
|||
|
|
color_threshold = maxpixvalue - (int)(0.3 * maxpixdiff);
|
|||
|
|
|
|||
|
|
if (color_threshold < 0) color_threshold = 0;
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int gfg = GetMonoColor(fg);
|
|||
|
|
int gbg = GetMonoColor(bg);
|
|||
|
|
|
|||
|
|
for (int i = 0; i < (probe_width * 2); i++)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
if (i % 2 == 1)
|
|||
|
|
x = x0 + i / 2;
|
|||
|
|
else
|
|||
|
|
x = x0 - i / 2;
|
|||
|
|
|
|||
|
|
if (x < 0) continue;
|
|||
|
|
if (x >= max_width) continue;
|
|||
|
|
|
|||
|
|
int index = CoordsToIndex(x, y0, bmpData.Stride);
|
|||
|
|
if (index < 0 || index > (bytes - 1)) break;
|
|||
|
|
c1 = Color.FromArgb(rgbValues[index + 2], rgbValues[index + 1], rgbValues[index]);
|
|||
|
|
|
|||
|
|
|
|||
|
|
gc1 = GetMonoColor(c1);
|
|||
|
|
|
|||
|
|
index = CoordsToIndex(x + 1, y0, bmpData.Stride);
|
|||
|
|
if (index < 0 || index > (bytes - 1)) break;
|
|||
|
|
c2 = Color.FromArgb(rgbValues[index + 2], rgbValues[index + 1], rgbValues[index]);
|
|||
|
|
|
|||
|
|
gc2 = GetMonoColor(c2);
|
|||
|
|
|
|||
|
|
if ((gc1 == gfg && gc2 == gbg) ||
|
|||
|
|
(gc1 == gbg && gc2 == gfg))
|
|||
|
|
{
|
|||
|
|
if (gc1 == gfg && gc2 == gbg) start_direction = 4;
|
|||
|
|
if (gc1 == gbg && gc2 == gfg) start_direction = 0;
|
|||
|
|
hitborder = true;
|
|||
|
|
x1 = x; y1 = y;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (Exception)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
if (!hitborder)
|
|||
|
|
{
|
|||
|
|
return "";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Color[] cn = new Color[8 * n];
|
|||
|
|
int count = 0; int countlimit = 10000;
|
|||
|
|
x = x1; y = y1;
|
|||
|
|
|
|||
|
|
|
|||
|
|
int diffx = 0, diffy = 0;
|
|||
|
|
int index1 = 0;
|
|||
|
|
while (!hitstart)
|
|||
|
|
{
|
|||
|
|
count++;
|
|||
|
|
|
|||
|
|
//fallback to prevent infinite loop
|
|||
|
|
if (count > countlimit)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
return "";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
//getting all the neighbours' pixel color
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
//processing top neighbours left to right
|
|||
|
|
for (int i = 0; i <= 2 * n; i++)
|
|||
|
|
{
|
|||
|
|
diffx = i - n;
|
|||
|
|
index1 = CoordsToIndex(x + diffx, y - n, bmpData.Stride);
|
|||
|
|
cn[i] = ((x + diffx) >= 0 && (x + diffx) < max_width && (y - n) >= 0 && (y - n) < max_height) ?
|
|||
|
|
Color.FromArgb(rgbValues[index1 + 2], rgbValues[index1 + 1], rgbValues[index1])
|
|||
|
|
: Color.Empty;
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//processing right neighbours top to bottom
|
|||
|
|
for (int i = 2 * n + 1; i < 4 * n; i++)
|
|||
|
|
{
|
|||
|
|
diffy = i - 3 * n;
|
|||
|
|
index1 = CoordsToIndex(x + n, y + diffy, bmpData.Stride);
|
|||
|
|
|
|||
|
|
cn[i] = ((x + n) >= 0 && (x + n) < max_width && (y + diffy) >= 0 && (y + diffy) < max_height) ?
|
|||
|
|
Color.FromArgb(rgbValues[index1 + 2], rgbValues[index1 + 1], rgbValues[index1])
|
|||
|
|
: Color.Empty;
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//processing bottom neighbours right to left
|
|||
|
|
for (int i = 4 * n; i <= 6 * n; i++)
|
|||
|
|
{
|
|||
|
|
diffx = i - 5 * n;
|
|||
|
|
index1 = CoordsToIndex(x - diffx, y + n, bmpData.Stride);
|
|||
|
|
|
|||
|
|
cn[i] = ((x - diffx) >= 0 && (x - diffx) < max_width && (y + n) >= 0 && (y + n) < max_height) ?
|
|||
|
|
Color.FromArgb(rgbValues[index1 + 2], rgbValues[index1 + 1], rgbValues[index1])
|
|||
|
|
: Color.Empty;
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//processing left neighbours bottom to top
|
|||
|
|
for (int i = 6 * n + 1; i < 8 * n; i++)
|
|||
|
|
{
|
|||
|
|
diffy = i - 7 * n;
|
|||
|
|
index1 = CoordsToIndex(x - n, y - diffy, bmpData.Stride);
|
|||
|
|
cn[i] = ((x - n) >= 0 && (x - n) < max_width && (y - diffy) >= 0 && (y - diffy) < max_height) ?
|
|||
|
|
Color.FromArgb(rgbValues[index1 + 2], rgbValues[index1 + 1], rgbValues[index1])
|
|||
|
|
: Color.Empty;
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
catch (Exception e)
|
|||
|
|
{
|
|||
|
|
MessageBox.Show(e.ToString());
|
|||
|
|
|
|||
|
|
return "";
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int index = 0;
|
|||
|
|
string tests = "";
|
|||
|
|
bool dir_found = false;
|
|||
|
|
|
|||
|
|
//find the first valid foreground pixel
|
|||
|
|
for (int i = start_direction; i < start_direction + (8 * n); i++)
|
|||
|
|
{
|
|||
|
|
index = i % (8 * n);
|
|||
|
|
|
|||
|
|
|
|||
|
|
if (!cn[index].Equals(Color.Empty))
|
|||
|
|
if (GetMonoColor(cn[index]) == gfg)
|
|||
|
|
{
|
|||
|
|
current_direction = index;
|
|||
|
|
dir_found = true;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
//if no foreground pixel found, just find the next valid pixel
|
|||
|
|
|
|||
|
|
if (!dir_found)
|
|||
|
|
for (int i = start_direction; i < start_direction + (8 * n); i++)
|
|||
|
|
{
|
|||
|
|
index = i % (8 * n);
|
|||
|
|
|
|||
|
|
|
|||
|
|
if (!cn[index].Equals(Color.Empty))
|
|||
|
|
{
|
|||
|
|
current_direction = index;
|
|||
|
|
dir_found = true;
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
// find the next direction to look for foreground pixels
|
|||
|
|
if ((index >= 0) && (index <= 2 * n))
|
|||
|
|
{
|
|||
|
|
diffx = index - n;
|
|||
|
|
x += diffx;
|
|||
|
|
y -= n;
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
if ((index > 2 * n) && (index < 4 * n))
|
|||
|
|
{
|
|||
|
|
diffy = index - 3 * n;
|
|||
|
|
x += n;
|
|||
|
|
y += diffy;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ((index >= 4 * n) && (index <= 6 * n))
|
|||
|
|
{
|
|||
|
|
diffx = index - 5 * n;
|
|||
|
|
x -= diffx;
|
|||
|
|
y += n;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ((index > 6 * n) && (index < 8 * n))
|
|||
|
|
{
|
|||
|
|
diffy = index - 7 * n;
|
|||
|
|
x -= n;
|
|||
|
|
y -= diffy;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
//store the found outline
|
|||
|
|
tests = x + "," + y + ";";
|
|||
|
|
|
|||
|
|
s = s + tests;
|
|||
|
|
|
|||
|
|
|
|||
|
|
start_direction = (current_direction + (4 * n + 1)) % (8 * n);
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
//adaptive stop condition
|
|||
|
|
|
|||
|
|
bool bMinCountOK = (n > 1) ? (count > (max_height / 5)) : (count > 10);
|
|||
|
|
|
|||
|
|
if (bMinCountOK && (Math.Abs(x - x1) < (n + 1) && (Math.Abs(y - y1) < (n + 1))))
|
|||
|
|
hitstart = true;
|
|||
|
|
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return s;
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
}
|