gpt4 book ai didi

c# - 从半透明位图创建 GraphicsPath

转载 作者:太空狗 更新时间:2023-10-29 21:18:52 26 4
gpt4 key购买 nike

我想创建一个 GraphicsPath 和一个点列表来形成位图非透明区域的轮廓。如果需要,我可以保证每张图片只有一个非透明像素的实体集合。因此,例如,我应该能够沿着像素的边缘顺时针或逆时针记录点并执行完整的闭环。

这个算法的速度并不重要。但是,如果我可以跳过一些点以减少更小和更不复杂的 GraphicsPath,则结果点的效率是次要的。

我将在下面列出我当前的代码,它可以完美地处理大多数图像。然而,一些更复杂的图像最终的路径似乎以错误的顺序连接。我想我知道为什么会这样,但我想不出解决方案。

public static Point[] GetOutlinePoints(Bitmap image)
{
List<Point> outlinePoints = new List<Point>();

BitmapData bitmapData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

byte[] originalBytes = new byte[image.Width * image.Height * 4];
Marshal.Copy(bitmapData.Scan0, originalBytes, 0, originalBytes.Length);

for (int x = 0; x < bitmapData.Width; x++)
{
for (int y = 0; y < bitmapData.Height; y++)
{
byte alpha = originalBytes[y * bitmapData.Stride + 4 * x + 3];

if (alpha != 0)
{
Point p = new Point(x, y);

if (!ContainsPoint(outlinePoints, p))
outlinePoints.Add(p);

break;
}
}
}

for (int y = 0; y < bitmapData.Height; y++)
{
for (int x = bitmapData.Width - 1; x >= 0; x--)
{
byte alpha = originalBytes[y * bitmapData.Stride + 4 * x + 3];

if (alpha != 0)
{
Point p = new Point(x, y);

if (!ContainsPoint(outlinePoints, p))
outlinePoints.Add(p);

break;
}
}
}

for (int x = bitmapData.Width - 1; x >= 0; x--)
{
for (int y = bitmapData.Height - 1; y >= 0; y--)
{
byte alpha = originalBytes[y * bitmapData.Stride + 4 * x + 3];

if (alpha != 0)
{
Point p = new Point(x, y);

if (!ContainsPoint(outlinePoints, p))
outlinePoints.Add(p);

break;
}
}
}

for (int y = bitmapData.Height - 1; y >= 0; y--)
{
for (int x = 0; x < bitmapData.Width; x++)
{
byte alpha = originalBytes[y * bitmapData.Stride + 4 * x + 3];

if (alpha != 0)
{
Point p = new Point(x, y);

if (!ContainsPoint(outlinePoints, p))
outlinePoints.Add(p);

break;
}
}
}

// Added to close the loop
outlinePoints.Add(outlinePoints[0]);

image.UnlockBits(bitmapData);

return outlinePoints.ToArray();
}

public static bool ContainsPoint(IEnumerable<Point> points, Point value)
{
foreach (Point p in points)
{
if (p == value)
return true;
}

return false;
}

当我把点变成路径时:

GraphicsPath outlinePath = new GraphicsPath();
outlinePath.AddLines(_outlinePoints);

这是一个显示我想要的示例。红色轮廓应该是一个点数组,可以将其制成 GraphicsPath 以执行命中检测、绘制轮廓笔并用画笔填充它。

Example of an Outline

最佳答案

就像你们两个描述的那样,您只需要找到第一个非透明点,然后沿着具有透明邻居的非透明像素移动。

此外,您必须保存已经访问过的点以及访问它们的频率,否则您将在无限循环中以相同的情况结束。如果该点没有已被访问过的邻居,则您必须沿可敬的方向返回每个点,直到再次出现未访问过的点。

就是这样。


查看旧版本的修订历史。

变化:

  • Point.EMPTY 被 Point(-1,-1) 或非透明像素取代在左上角导致无限循环
  • 检查图像边界处的边界点

class BorderFinder {

int stride = 0;
int[] visited = null;
byte[] bytes = null;
PointData borderdata = null;
Size size = Size.Empty;
bool outside = false;
Point zeropoint = new Point(-1,-1);

public List<Point[]> Find(Bitmap bmp, bool outside = true) {
this.outside = outside;
List<Point> border = new List<Point>();
BitmapData bmpdata = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

stride = bmpdata.Stride;
bytes = new byte[bmp.Width * bmp.Height * 4];
size = bmp.Size;


Marshal.Copy(bmpdata.Scan0, bytes, 0, bytes.Length);


// Get all Borderpoint
borderdata = getBorderData(bytes);

bmp.UnlockBits(bmpdata);

List<List<Point>> regions = new List<List<Point>>();

//Loop until no more borderpoints are available
while (borderdata.PointCount > 0) {
List<Point> region = new List<Point>();

//if valid is false the region doesn't close
bool valid = true;

//Find the first borderpoint from where whe start crawling
Point startpos = getFirstPoint(borderdata);

//we need this to know if and how often we already visted the point.
//we somtime have to visit a point a second time because we have to go backward until a unvisted point is found again
//for example if we go int a narrow 1px hole
visited = new int[bmp.Size.Width * bmp.Size.Height];

region.Add(startpos);

//Find the next possible point
Point current = getNextPoint(startpos);

if (current != zeropoint) {
visited[current.Y * bmp.Width + current.X]++;
region.Add(current);
}

//May occure with just one transparent pixel without neighbors
if (current == zeropoint)
valid = false;

//Loop until the area closed or colsing the area wasn't poosible
while (!current.Equals(startpos) && valid) {
var pos = current;
//Check if the area was aready visited
if (visited[current.Y * bmp.Width + current.X] < 2) {
current = getNextPoint(pos);
visited[pos.Y * bmp.Width + pos.X]++;
//If no possible point was found, search in reversed direction
if (current == zeropoint)
current = getNextPointBackwards(pos);
} else { //If point was already visited, search in reversed direction
current = getNextPointBackwards(pos);
}

//No possible point was found. Closing isn't possible
if (current == zeropoint) {
valid = false;
break;
}

visited[current.Y * bmp.Width + current.X]++;

region.Add(current);
}
//Remove point from source borderdata
foreach (var p in region) {
borderdata.SetPoint(p.Y * bmp.Width + p.X, false);
}
//Add region if closing was possible
if (valid)
regions.Add(region);
}

//Checks if Region goes the same way back and trims it in this case
foreach (var region in regions) {
int duplicatedpos = -1;

bool[] duplicatecheck = new bool[size.Width * size.Height];
int length = region.Count;
for (int i = 0; i < length; i++) {
var p = region[i];
if (duplicatecheck[p.Y * size.Width + p.X]) {
duplicatedpos = i - 1;
break;
}
duplicatecheck[p.Y * size.Width + p.X] = true;
}

if (duplicatedpos == -1)
continue;

if (duplicatedpos != ((region.Count - 1) / 2))
continue;

bool reversed = true;

for (int i = 0; i < duplicatedpos; i++) {
if (region[duplicatedpos - i - 1] != region[duplicatedpos + i + 1]) {
reversed = false;
break;
}
}

if (!reversed)
continue;

region.RemoveRange(duplicatedpos + 1, region.Count - duplicatedpos - 1);
}

List<List<Point>> tempregions = new List<List<Point>>(regions);
regions.Clear();

bool connected = true;
//Connects region if possible
while (connected) {
connected = false;
foreach (var region in tempregions) {
int connectionpos = -1;
int connectionregion = -1;
Point pointstart = region.First();
Point pointend = region.Last();
for (int ir = 0; ir < regions.Count; ir++) {
var otherregion = regions[ir];
if (region == otherregion)
continue;

for (int ip = 0; ip < otherregion.Count; ip++) {
var p = otherregion[ip];
if ((isConnected(pointstart, p) && isConnected(pointend, p)) ||
(isConnected(pointstart, p) && isConnected(pointstart, p))) {
connectionregion = ir;
connectionpos = ip;
}

if ((isConnected(pointend, p) && isConnected(pointend, p))) {
region.Reverse();
connectionregion = ir;
connectionpos = ip;
}
}

}

if (connectionpos == -1) {
regions.Add(region);
} else {
regions[connectionregion].InsertRange(connectionpos, region);
}

}

tempregions = new List<List<Point>>(regions);
regions.Clear();
}

List<Point[]> returnregions = new List<Point[]>();

foreach (var region in tempregions)
returnregions.Add(region.ToArray());

return returnregions;
}

private bool isConnected(Point p0, Point p1) {

if (p0.X == p1.X && p0.Y - 1 == p1.Y)
return true;

if (p0.X + 1 == p1.X && p0.Y - 1 == p1.Y)
return true;

if (p0.X + 1 == p1.X && p0.Y == p1.Y)
return true;

if (p0.X + 1 == p1.X && p0.Y + 1 == p1.Y)
return true;

if (p0.X == p1.X && p0.Y + 1 == p1.Y)
return true;

if (p0.X - 1 == p1.X && p0.Y + 1 == p1.Y)
return true;

if (p0.X - 1 == p1.X && p0.Y == p1.Y)
return true;

if (p0.X - 1 == p1.X && p0.Y - 1 == p1.Y)
return true;

return false;
}

private Point getNextPoint(Point pos) {
if (pos.Y > 0) {
int x = pos.X;
int y = pos.Y - 1;
if ((ValidPoint(x, y)) && HasNeighbor(x, y)) {
if (visited[y * size.Width + x] == 0) {
return new Point(x, y);
}
}
}

if (pos.Y > 0 && pos.X < size.Width - 1) {
int x = pos.X + 1;
int y = pos.Y - 1;
if ((ValidPoint(x, y)) && HasNeighbor(x, y)) {
if (visited[y * size.Width + x] == 0) {
return new Point(x, y);
}
}
}

if (pos.X < size.Width - 1) {
int x = pos.X + 1;
int y = pos.Y;
if ((ValidPoint(x, y)) && HasNeighbor(x, y)) {
if (visited[y * size.Width + x] == 0) {
return new Point(x, y);
}
}
}

if (pos.X < size.Width - 1 && pos.Y < size.Height - 1) {
int x = pos.X + 1;
int y = pos.Y + 1;
if ((ValidPoint(x, y)) && HasNeighbor(x, y)) {
if (visited[y * size.Width + x] == 0) {
return new Point(x, y);
}
}
}

if (pos.Y < size.Height - 1) {
int x = pos.X;
int y = pos.Y + 1;
if ((ValidPoint(x, y)) && HasNeighbor(x, y)) {
if (visited[y * size.Width + x] == 0) {
return new Point(x, y);
}
}
}


if (pos.Y < size.Height - 1 && pos.X > 0) {
int x = pos.X - 1;
int y = pos.Y + 1;
if ((ValidPoint(x, y)) && HasNeighbor(x, y)) {
if (visited[y * size.Width + x] == 0) {
return new Point(x, y);
}
}
}

if (pos.X > 0) {
int x = pos.X - 1;
int y = pos.Y;
if ((ValidPoint(x, y)) && HasNeighbor(x, y)) {
if (visited[y * size.Width + x] == 0) {
return new Point(x, y);
}
}
}

if (pos.X > 0 && pos.Y > 0) {
int x = pos.X - 1;
int y = pos.Y - 1;
if ((ValidPoint(x, y)) && HasNeighbor(x, y)) {
if (visited[y * size.Width + x] == 0) {
return new Point(x, y);
}
}
}


return zeropoint;
}

private Point getNextPointBackwards(Point pos) {
Point backpoint = zeropoint;

int trys = 0;

if (pos.X > 0 && pos.Y > 0) {
int x = pos.X - 1;
int y = pos.Y - 1;
if (ValidPoint(x, y) && HasNeighbor(x, y)) {
if (visited[y * size.Width + x] == 0) {
return new Point(x, y);
}
if (backpoint == zeropoint || trys > visited[y * size.Width + x]) {
backpoint = new Point(x, y);
trys = visited[y * size.Width + x];
}
}
}

if (pos.X > 0) {
int x = pos.X - 1;
int y = pos.Y;
if ((ValidPoint(x, y)) && HasNeighbor(x, y)) {
if (visited[y * size.Width + x] == 0) {
return new Point(x, y);
}
if (backpoint == zeropoint || trys > visited[y * size.Width + x]) {
backpoint = new Point(x, y);
trys = visited[y * size.Width + x];
}
}
}

if (pos.Y < size.Height - 1 && pos.X > 0) {
int x = pos.X - 1;
int y = pos.Y + 1;
if ((ValidPoint(x, y)) && HasNeighbor(x, y)) {
if (visited[y * size.Width + x] == 0) {
return new Point(x, y);
}
if (backpoint == zeropoint || trys > visited[y * size.Width + x]) {
backpoint = new Point(x, y);
trys = visited[y * size.Width + x];
}
}
}

if (pos.Y < size.Height - 1) {
int x = pos.X;
int y = pos.Y + 1;
if ((ValidPoint(x, y)) && HasNeighbor(x, y)) {
if (visited[y * size.Width + x] == 0) {
return new Point(x, y);
}
if (backpoint == zeropoint || trys > visited[y * size.Width + x]) {
backpoint = new Point(x, y);
trys = visited[y * size.Width + x];
}
}
}


if (pos.X < size.Width - 1 && pos.Y < size.Height - 1) {
int x = pos.X + 1;
int y = pos.Y + 1;
if ((ValidPoint(x, y)) && HasNeighbor(x, y)) {
if (visited[y * size.Width + x] == 0) {
return new Point(x, y);
}
if (backpoint == zeropoint || trys > visited[y * size.Width + x]) {
backpoint = new Point(x, y);
trys = visited[y * size.Width + x];
}
}
}


if (pos.X < size.Width - 1) {
int x = pos.X + 1;
int y = pos.Y;
if ((ValidPoint(x, y)) && HasNeighbor(x, y)) {
if (visited[y * size.Width + x] == 0) {
return new Point(x, y);
}
if (backpoint == zeropoint || trys > visited[y * size.Width + x]) {
backpoint = new Point(x, y);
trys = visited[y * size.Width + x];
}
}
}

if (pos.Y > 0 && pos.X < size.Width - 1) {
int x = pos.X + 1;
int y = pos.Y - 1;
if ((ValidPoint(x, y)) && HasNeighbor(x, y)) {
if (visited[y * size.Width + x] == 0) {
return new Point(x, y);
}
if (backpoint == zeropoint || trys > visited[y * size.Width + x]) {
backpoint = new Point(x, y);
trys = visited[y * size.Width + x];
}
}
}


if (pos.Y > 0) {
int x = pos.X;
int y = pos.Y - 1;
if ((ValidPoint(x, y)) && HasNeighbor(x, y)) {
if (visited[y * size.Width + x] == 0) {
return new Point(x, y);
}
if (backpoint == zeropoint || trys > visited[y * size.Width + x]) {
backpoint = new Point(x, y);
trys = visited[y * size.Width + x];
}
}
}

return backpoint;
}

private bool ValidPoint(int x, int y) {
return (borderdata[y * size.Width + x]);
}

private bool HasNeighbor(int x, int y) {
if (y > 0) {
if (!borderdata[(y - 1) * size.Width + x]) {
return true;
}
} else if (ValidPoint(x, y)) {
return true;
}

if (x < size.Width - 1) {
if (!borderdata[y * size.Width + (x + 1)]) {
return true;
}
} else if (ValidPoint(x, y)) {
return true;
}

if (y < size.Height - 1) {
if (!borderdata[(y + 1) * size.Width + x]) {
return true;
}
} else if (ValidPoint(x, y)) {
return true;
}

if (x > 0) {
if (!borderdata[y * size.Width + (x - 1)]) {
return true;
}
} else if (ValidPoint(x, y)) {
return true;
}

return false;
}

private Point getFirstPoint(PointData data) {
Point startpos = zeropoint;
for (int y = 0; y < size.Height; y++) {
for (int x = 0; x < size.Width; x++) {
if (data[y * size.Width + x]) {
startpos = new Point(x, y);
return startpos;
}
}
}
return startpos;
}

private PointData getBorderData(byte[] bytes) {

PointData isborderpoint = new PointData(size.Height * size.Width);
bool prevtrans = false;
bool currenttrans = false;
for (int y = 0; y < size.Height; y++) {
prevtrans = false;
for (int x = 0; x <= size.Width; x++) {
if (x == size.Width) {
if (!prevtrans) {
isborderpoint.SetPoint(y * size.Width + x - 1, true);
}
continue;
}
currenttrans = bytes[y * stride + x * 4 + 3] == 0;
if (x == 0 && !currenttrans)
isborderpoint.SetPoint(y * size.Width + x, true);
if (prevtrans && !currenttrans)
isborderpoint.SetPoint(y * size.Width + x - 1, true);
if (!prevtrans && currenttrans && x != 0)
isborderpoint.SetPoint(y * size.Width + x, true);
prevtrans = currenttrans;
}
}
for (int x = 0; x < size.Width; x++) {
prevtrans = false;
for (int y = 0; y <= size.Height; y++) {
if (y == size.Height) {
if (!prevtrans) {
isborderpoint.SetPoint((y - 1) * size.Width + x, true);
}
continue;
}
currenttrans = bytes[y * stride + x * 4 + 3] == 0;
if(y == 0 && !currenttrans)
isborderpoint.SetPoint(y * size.Width + x, true);
if (prevtrans && !currenttrans)
isborderpoint.SetPoint((y - 1) * size.Width + x, true);
if (!prevtrans && currenttrans && y != 0)
isborderpoint.SetPoint(y * size.Width + x, true);
prevtrans = currenttrans;
}
}
return isborderpoint;
}
}

class PointData {
bool[] points = null;
int validpoints = 0;
public PointData(int length) {
points = new bool[length];
}

public int PointCount {
get {
return validpoints;
}
}

public void SetPoint(int pos, bool state) {
if (points[pos] != state) {
if (state)
validpoints++;
else
validpoints--;
}
points[pos] = state;
}
public bool this[int pos] {
get {
return points[pos];
}
}


}

关于c# - 从半透明位图创建 GraphicsPath,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9752410/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com