今天有个需求,识别图片中的圆的圆心,已知都是完整的圆,而且没有半截的圆,并且圆的颜色都一样,没有其他的干扰因素,要寻找这些圆的圆心,稍微思考了一下,这个问题其实没有那么复杂,因为都是完整的圆,那么就可以根据圆的性质入手,2条经过圆心的线就可以确定一个圆的圆心了,那么如何来确定这两条线呢?
圆还有一个性质就是圆是对称的,所以任意的穿过圆的线的两个交点的中心就是处在垂直于这条线并且经过圆心的线上面,同理取2条线,然后取交点,就是圆心了
下面的代码的实现和上述理论的基本实现,但是没有做优化,请自行优化
void CircleCenter(Texture2D tex) {
//在这里做一遍圆心识别算法
bool[, ] isCircleColor = new bool[tex.width, tex.height];
for (int i = 0; i < tex.width; i++) {
for (int j = 0; j < tex.height; j++) {
isCircleColor[i, j] = (Init.HexToColor(0x878787) == tex.GetPixel(i, j));
}
}
Stack<Vector2Int> points = new Stack<Vector2Int>();
//识别出来是否是圆形颜色的区域之后 开始做横向的扫描
for (int y = 0; y < tex.height; y++) {
for (int x = 1; x < tex.width - 1; x++) {
//取点
if (isCircleColor[x, y]) {
//判断是不是刚好遇到了左切点
if (isCircleColor[x + 1, y] && !isCircleColor[x - 1, y]) {
//不是切点的时候就说明后面还有点 入栈
points.Push(new Vector2Int(x, y));
} else if (!isCircleColor[x + 1, y] && isCircleColor[x - 1, y]) {
if (points.Count != 0) {
//右切点 的时候 出栈并处理2点中间的数据
Vector2Int startPoint = points.Pop();
for (int clearX = startPoint.x; clearX <= x; clearX++) {
isCircleColor[clearX, y] = false;
}
//然后把中点点亮
isCircleColor[(startPoint.x + x) / 2, y] = true;
}
} else if (!isCircleColor[x + 1, y] && !isCircleColor[x - 1, y]) {
isCircleColor[x, y] = false;
}
//刚好遇到的了切点,那就不用管了
}
}
}
// 开始竖向的扫描
const int rectwidth = 2;
for (int x = 0; x < tex.width; x++) {
for (int y = 1; y < tex.height; y++) {
//每次检测x 到 x + rectwidth 的矩形范围 考虑上一轮中可能有误差 所以 要先把误差给消除
Vector2Int temp = CheckPoint(isCircleColor, x, y, rectwidth);
Vector2Int lowTemp = CheckLowPoint(isCircleColor, x, y, rectwidth);
if (temp == Vector2Int.zero || lowTemp == Vector2Int.zero) {
continue;
}
isCircleColor[temp.x, temp.y] = false;
isCircleColor[lowTemp.x, temp.y] = true;
}
}
// // 真正的竖向扫描
for (int x = 0; x < tex.width; x++) {
for (int y = 1; y < tex.height - 1; y++) {
//取点
if (isCircleColor[x, y]) {
//判断是不是刚好遇到了左切点
if (isCircleColor[x, y + 1] && !isCircleColor[x, y - 1]) {
//不是切点的时候就说明后面还有点 入栈
points.Push(new Vector2Int(x, y));
} else if (!isCircleColor[x, y + 1] && isCircleColor[x, y - 1]) {
if (points.Count != 0) {
//右切点 的时候 出栈并处理2点中间的数据
Vector2Int startPoint = points.Pop();
for (int clearY = startPoint.y; clearY <= y; clearY++) {
isCircleColor[x, clearY] = false;
}
//然后把中点点亮
isCircleColor[x, (startPoint.y + y) / 2] = true;
}
} else if (!isCircleColor[x, y + 1] && !isCircleColor[x, y - 1]) {
isCircleColor[x, y] = false;
}
//刚好遇到的了切点,那就不用管了
}
}
}
for (int i = 0; i < tex.width; i++) {
for (int j = 0; j < tex.height; j++) {
if (isCircleColor[i, j]) {
tex.SetPixel(i, j, Color.red);
}
}
}
tex.Apply();
}
/// <summary>
/// 检测下一行是否有点
/// </summary>
Vector2Int CheckLowPoint(bool[, ] isCircleColor, int x, int y, int rectwidth) {
if (y - 1 < 0) {
return Vector2Int.zero;
}
int width = isCircleColor.GetLength(0);
int height = isCircleColor.GetLength(1);
for (int i = Mathf.Max(x - rectwidth, 0); i < Mathf.Min(x + rectwidth, width); i++) {
if (isCircleColor[i, y - 1]) {
return new Vector2Int(i, y);
}
}
return Vector2Int.zero;
}
/// <summary>
/// 检测上一行是否有点
/// </summary>
Vector2Int CheckHighPoint(bool[, ] isCircleColor, int x, int y, int rectwidth) {
int width = isCircleColor.GetLength(0);
int height = isCircleColor.GetLength(1);
if (y + 1 >= height) {
return Vector2Int.zero;
}
for (int i = Mathf.Max(x - rectwidth, 0); i < Mathf.Min(x + rectwidth, width); i++) {
if (isCircleColor[i, y + 1]) {
return new Vector2Int(i, y);
}
}
return Vector2Int.zero;
}
/// <summary>
/// 检测当前一行是否有点
/// </summary>
Vector2Int CheckPoint(bool[, ] isCircleColor, int x, int y, int rectwidth) {
int width = isCircleColor.GetLength(0);
int height = isCircleColor.GetLength(1);
for (int i = Mathf.Max(x - rectwidth, 0); i < Mathf.Min(x + rectwidth, width); i++) {
if (isCircleColor[i, y]) {
return new Vector2Int(i, y);
}
}
return Vector2Int.zero;
}