/* < >
*
* :
* (x, y) ,
* ,
* [xLeft, xRight],
* 、 ,
* 。 , 。
*
* :
* (1) , (x, y) ;
* (2) , , (x, y),y ;
* (3) (x, y) , 、 , 。 、 xLeft xRight;
* (4) y - 1 y + 1 [xLeft, xRight] ,
* xLeft xRight , , , ,
* (2) ;
*
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.IO;
using System.Drawing.Imaging;
namespace MagicWandTool
{
class MagicWandTool
{
#region Private Field
private int _iThresholdValue = 0; // ([0,255])
private int _iImageWidth = 0; //
private int _iImageHeight = 0; //
private IntPtr _ptrSourcePoint = IntPtr.Zero; //
private IntPtr _ptrImageStartPoint = IntPtr.Zero; //
private Dictionary<string, Point> _dicScannedSeed = new Dictionary<string, Point>(); // ( , , , , )
private Dictionary<string, Point> _dicBorderRegion = new Dictionary<string, Point>(); // ( Dictionary , , List, List )
private Stack<Point> _stackSeed = new Stack<Point>(); //
#endregion
#region Constructor
public MagicWandTool()
{
this._stackSeed = new Stack<Point>();
this._dicBorderRegion = new Dictionary<string, Point>();
this._dicScannedSeed = new Dictionary<string, Point>();
}
#endregion
#region Methods
#region Public Methods
public Point[] GetSelectRange(Image sourceImage,Point sourcePoint,int iThrehslodValue)
{
List<Point> listRange = new List<Point>();
try
{
#region
Bitmap sourceBitmap = new Bitmap(sourceImage);
this._iThresholdValue = iThrehslodValue;
this._iImageHeight = sourceBitmap.Height;
this._iImageWidth = sourceBitmap.Width;
BitmapData sourceBitmapData = sourceBitmap.LockBits(new Rectangle(0, 0, this._iImageWidth, this._iImageHeight), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
unsafe
{
this._ptrImageStartPoint = sourceBitmapData.Scan0;
this._ptrSourcePoint = (IntPtr)((byte*)(this._ptrImageStartPoint) + (sourcePoint.Y * this._iImageWidth + sourcePoint.X) * 4);
}
#endregion
#region
if (!this.StartScanLine(sourcePoint))
{
this._dicBorderRegion.Clear();
Console.WriteLine("GetSelectRange.StartScanLine: Failed.");
}
#endregion
#region ,
sourceBitmap.UnlockBits(sourceBitmapData);
foreach (Point tp in this._dicBorderRegion.Values)
{
listRange.Add(tp);
}
#endregion
}
catch (Exception ex)
{
Console.WriteLine("GetSelectRange.Exception: " + ex.Message);
listRange.Clear();
}
return listRange.ToArray();
}
public Rectangle GetSelectRectangle(Image sourceImage, Point sourcePoint, int iThrehslodValue)
{
Rectangle rectangle = new Rectangle();
try
{
#region
Bitmap sourceBitmap = new Bitmap(sourceImage);
this._iThresholdValue = iThrehslodValue;
this._iImageHeight = sourceBitmap.Height;
this._iImageWidth = sourceBitmap.Width;
BitmapData sourceBitmapData = sourceBitmap.LockBits(new Rectangle(0, 0, this._iImageWidth, this._iImageHeight), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
unsafe
{
this._ptrImageStartPoint = sourceBitmapData.Scan0;
this._ptrSourcePoint = (IntPtr)((byte*)(this._ptrImageStartPoint) + (sourcePoint.Y * this._iImageWidth + sourcePoint.X) * 4);
}
#endregion
#region
if (!this.StartScanLine(sourcePoint))
{
this._dicBorderRegion.Clear();
Console.WriteLine("GetSelectRectangle.StartScanLine: Failed.");
}
#endregion
#region ,
sourceBitmap.UnlockBits(sourceBitmapData);
// , , , ,
int iMinX = 0;
int iMaxX = 0;
int iMinY = 0;
int iMaxY = 0;
foreach (Point tp in this._dicBorderRegion.Values)
{
if (tp.X < iMinX)
{
iMinX = tp.X;
}
if (iMaxX < tp.X)
{
iMaxX = tp.X;
}
if (tp.X < iMinX)
{
iMinX = tp.X;
}
if (iMaxX < tp.X)
{
iMaxX = tp.X;
}
}
rectangle = new Rectangle(iMinX, iMinY, iMaxX - iMinX, iMaxY - iMinY);
#endregion
}
catch (Exception ex)
{
Console.WriteLine("GetSelectRectangle.Exception: " + ex.Message);
rectangle = new Rectangle();
}
return rectangle;
}
#endregion
#region Private Methods
private bool StartScanLine(Point sourcePoint)
{
try
{
this._dicBorderRegion.Clear();
this._dicScannedSeed.Clear();
this._stackSeed.Clear();
this._stackSeed.Push(sourcePoint); // ,
Point tempPoint = new Point();
Point seed = new Point();
int iCount = 0;
int iLeftX = 0;
int iRightX = 0;
while (0 != this._stackSeed.Count)
{
seed = this._stackSeed.Pop(); // ,
this._dicScannedSeed[seed.ToString()] = seed;
iCount = 0;
if (seed.X - 1 >= 0)
{
iCount = ScanLineLeft(new Point(seed.X - 1, seed.Y)); // , ,
}
//
iLeftX = seed.X - iCount;
iCount = 0;
if (seed.X + 1 < this._iImageWidth)
{
iCount = ScanLineRight(new Point(seed.X + 1, seed.Y)); // , ,
}
//
iRightX = seed.X + iCount;
if (seed.Y - 1 > 0)
{
ScanLineNewSeed(new Point(iLeftX, seed.Y - 1), new Point(iRightX, seed.Y - 1)); // , ,
}
else
{
// ,
tempPoint.Y = 0;
for (int i = iLeftX; i <= iRightX; i++)
{
tempPoint.X = i;
this._dicBorderRegion[tempPoint.ToString()] = tempPoint;
}
}
if (seed.Y + 1 < this._iImageHeight)
{
ScanLineNewSeed(new Point(iLeftX, seed.Y + 1), new Point(iRightX, seed.Y + 1));
}
else
{
// ,
tempPoint.Y = this._iImageHeight - 1;
for (int i = iLeftX; i <= iRightX; i++)
{
tempPoint.X = i;
this._dicBorderRegion[tempPoint.ToString()] = tempPoint;
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("StartScanLine.Exception: " + ex.Message);
return false;
}
return true;
}
private int ScanLineLeft(Point seedPoint)
{
int iCount = 0;
Point currentCheckPoint = new Point();
currentCheckPoint.Y = seedPoint.Y;
for (int i = seedPoint.X; i >= 0; i--)
{
currentCheckPoint.X = i;
if (!this.IsPixelValid(currentCheckPoint))
{
break;
}
iCount++;
// ,
if (0 == i)
{
this._dicBorderRegion[currentCheckPoint.ToString()] = currentCheckPoint;
}
}
return iCount;
}
private int ScanLineRight(Point seedPoint)
{
int iCount = 0;
Point currentCheckPoint = new Point();
currentCheckPoint.Y = seedPoint.Y;
for (int i = seedPoint.X; i < this._iImageWidth; i++)
{
currentCheckPoint.X = i;
if (!this.IsPixelValid(currentCheckPoint))
{
if (i - 1 >= 0)
{
// , , ,
currentCheckPoint.X = i - 1;
this._dicScannedSeed[currentCheckPoint.ToString()] = currentCheckPoint;
}
break;
}
iCount++;
// ,
if (this._iImageWidth - 1 == i)
{
this._dicBorderRegion[currentCheckPoint.ToString()] = currentCheckPoint;
}
}
return iCount;
}
private void ScanLineNewSeed(Point leftPoint, Point rightPoint)
{
bool bIsFindNewSeed = false;
int iCurrentX = leftPoint.X;
Point tempPoint = new Point();
tempPoint.Y = leftPoint.Y;
// [leftPoint.X,rightPoint.X] , , rightPoint.X,
while (iCurrentX <= rightPoint.X)
{
bIsFindNewSeed = false;
// , , (iCurrentX - 1) .
while (iCurrentX <= rightPoint.X)
{
tempPoint.X = iCurrentX;
if (!this.IsPixelValid(tempPoint))
{
break;
}
bIsFindNewSeed = true;
iCurrentX++;
}
if (bIsFindNewSeed)
{
tempPoint.X = iCurrentX - 1;
// ,
if (!this._dicScannedSeed.ContainsKey(tempPoint.ToString()))
{
this._stackSeed.Push(tempPoint);
}
}
/* ( )*/
// , : , :
// , ,
// , iCurrentX , , , iCurrentX++,
iCurrentX++;
while (iCurrentX <= rightPoint.X)
{
tempPoint.X = iCurrentX;
if (this.IsPixelValid(tempPoint))
{
// ,
break;
}
// ,
iCurrentX++;
}
}
}
private bool IsPixelValid(Point currentCheckPoint)
{
try
{
if (255 == this._iThresholdValue)
{
return true;
}
unsafe
{
byte *pCurrentColor = (byte *)(this._ptrImageStartPoint);
pCurrentColor += (currentCheckPoint.Y * this._iImageWidth + currentCheckPoint.X) * 4;
if ((((((byte *)(this._ptrSourcePoint))[0] - this._iThresholdValue) <= pCurrentColor[0]) && (pCurrentColor[0] <= ((byte *)(this._ptrSourcePoint))[0] + this._iThresholdValue))
&& (((((byte*)(this._ptrSourcePoint))[1] - this._iThresholdValue) <= pCurrentColor[1]) && (pCurrentColor[1] <= ((byte*)(this._ptrSourcePoint))[1] + this._iThresholdValue))
&& (((((byte*)(this._ptrSourcePoint))[2] - this._iThresholdValue) <= pCurrentColor[2]) && (pCurrentColor[2] <= ((byte*)(this._ptrSourcePoint))[2] + this._iThresholdValue))
&& (((((byte*)(this._ptrSourcePoint))[3] - this._iThresholdValue) <= pCurrentColor[3]) && (pCurrentColor[3] <= ((byte*)(this._ptrSourcePoint))[3] + this._iThresholdValue)))
{
return true;
}
else
{
this._dicBorderRegion[currentCheckPoint.ToString()] = currentCheckPoint;
return false;
}
}
}
catch (Exception ex)
{
Console.WriteLine("IsPixelValid.Exception: " + ex + "; currentCheckPoint:" + currentCheckPoint.ToString());
return false;
}
}
#endregion
#endregion
}
}