不使用 Aforge 或 OpenCv 的圆检测
Circle Detection without use Aforge Or OpenCv
我想在没有库的情况下检测位图中的圆圈。首先,我使用 otsu thresold 进行二值化。二值化后我使用拉普拉斯边缘检测。现在我的位图中有一个圆圈。我怎样才能检测到这个圆圈。
注意:我试图画一个圆并浮动 x 和 y 坐标。但是这种方式太慢了,而且这种方式存在半径问题。因为,如果我的 tempCircle 的半径 = 10 和真实圆的半径 = 9 那么我的算法找不到圆。
BitmapProcess bmpPro = new BitmapProcess((Bitmap)(pictureBox1.Image));
bmpPro.LockBits();
int black = 0, temp = 0,fixX=0,fixY=0;
Bitmap bmp = (Bitmap)pictureBox1.Image;
Graphics g = this.pictureBox1.CreateGraphics();
Pen pen = new Pen(Color.Red, 10);
int x = 0, y = 0;
for (int a = 0; a < pictureBox1.Image.Width - 1; a += 1)
{
for (int b = 0; b < pictureBox1.Image.Height - 1; b++)
{
double radius = 10;
temp = 0;
for (double i = 0.0; i < 360.0; i += 1)
{
double angle = i * System.Math.PI / 180;
x = (int)(a + radius * System.Math.Cos(angle));
y = (int)(b + radius * System.Math.Sin(angle));
Color aa = bmpPro.GetPixel(Math.Abs(x), Math.Abs(y));
if (bmpPro.GetPixel(Math.Abs(x), Math.Abs(y))!=Color.Black) temp++;
}
if (temp > black)
{
black = temp;
fixX = a;
fixY = b;
}
}
}
g.DrawEllipse(pen,fixX,fixY,50,50);
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace LockBitBitmap
{
class BitmapProcess
{
public int b = 0;
int TotalPixelLocked;
Bitmap source = null; //kaynak bmp
IntPtr Iptr = IntPtr.Zero; //baslangıc adresi
BitmapData bitmapData = null;
public byte[] Pixels { get; set; }
public int Depth { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public BitmapProcess(Bitmap source)
{
this.source = source; //bitmapı dısardan al
}
public void LockBits()
{
//resmin en ve boyunu al
Width = source.Width;
Height = source.Height;
//kilit için rectangle olustur
Rectangle rect = new Rectangle(0,0,Width,Height);
//kaynak bitmap ın pixel formatını al
Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat);
//pixel basına bit sayısını bul(Bpp)
if (Depth != 8 && Depth != 24 && Depth != 32)
{
return; //bit türü desteklenmiyor...
}
//bitmapı kilitle ve veriyi döndür...
bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite, source.PixelFormat);
//kilitlenecek pixel sayısını al
TotalPixelLocked = Math.Abs(bitmapData.Stride) * source.Height;
//pixel verilerini tutmak için byte dizisi olustur
int step = Depth / 8;
Pixels = new byte[TotalPixelLocked * step];
Iptr = bitmapData.Scan0;
//verileri pointerden diziye aktar
Marshal.Copy(Iptr, Pixels, 0, TotalPixelLocked);
}
public Color GetPixel(int x, int y)
{
Color clr = Color.Empty; //boş renk
//renkli nesne sayısını al
int cCount = Depth / 8;
//istenen pixelin baslangıc adresini bul
int i = y * bitmapData.Stride + x * cCount;
if (i > (Pixels.Length - cCount))
{
throw new IndexOutOfRangeException("index out of range ( dizi adresi geçersiz)");
}
if (Depth == 32) //r g b a
{
byte b = Pixels[i];
byte g = Pixels[i + 1];
byte r = Pixels[i + 2];
byte a = Pixels[i + 3];
clr = Color.FromArgb(a,r,g,b);
}
if (Depth == 24) //r g b
{
byte b = Pixels[i];
byte g = Pixels[i + 1];
byte r = Pixels[i + 2];
clr = Color.FromArgb(r, g, b);
}
if (Depth == 8) // r g b hepsi aynı
{
byte c = Pixels[i];
clr = Color.FromArgb(c,c,c);
}
return clr;
}
public void SetPixel(int x, int y, Color color)
{
// renkli nesne sayısı
int cCount = Depth / 8;
// baslangıc indexini bul
//int i = ((y * Width) + x) * cCount;
int i = y * bitmapData.Stride + x * cCount;
if (i > (Pixels.Length - cCount))
{
throw new IndexOutOfRangeException("index out of range ( dizi adresi geçersiz)");
}
if (Depth == 32) // r,g,b, (alpha)
{
Pixels[i] = color.B;
Pixels[i + 1] = color.G;
Pixels[i + 2] = color.R;
Pixels[i + 3] = color.A;
}
if (Depth == 24) // r,g,b
{
Pixels[i] = color.B;
Pixels[i + 1] = color.G;
Pixels[i + 2] = color.R;
b++;
}
if (Depth == 8)//r g b hepsi aynı
{
Pixels[i] = color.B;
}
}
public Bitmap giveBitmap()
{
System.Runtime.InteropServices.Marshal.Copy(Pixels, 0, Iptr, TotalPixelLocked);
source.UnlockBits(bitmapData);
return source;
}
}
}
不如看看谢永红和季强的论文"A NEW EFFICIENT ELLIPSE DETECTION METHOD"。 link
我知道它的标题中有椭圆,但你肯定知道 cricle 是非常特殊的椭圆 ;)。
提示:您处理的下一步应该是 聚类 您的边缘像素 - 这样您就会知道哪些像素正在创建 object。我们知道 object 有一些 属性 并且您应该确定哪些 object 属性使 object 成为一个圆圈 - 然后实现这些 properties-based 在你的代码中过滤。
如果图片中只有几个圆圈,我建议使用 RANSAC。
简要说明:从你的边缘像素中,随机选取 3 个点,find the equation of the circle 适合这些点。在所有剩余的边缘点上使用这个方程,看看有多少边缘点位于圆上,即有多少满足方程。重复这个过程N次(N可以根据各种因素决定,包括边缘点的数量、圆的数量等),记录inliers(位于圆上的点)数量最多的迭代。这个最大迭代对应的方程就是你的最佳拟合圆。
如果图像中有多个圆圈,请在删除或屏蔽掉所有符合先前找到的圆圈的点后重复上述步骤。
我想在没有库的情况下检测位图中的圆圈。首先,我使用 otsu thresold 进行二值化。二值化后我使用拉普拉斯边缘检测。现在我的位图中有一个圆圈。我怎样才能检测到这个圆圈。
注意:我试图画一个圆并浮动 x 和 y 坐标。但是这种方式太慢了,而且这种方式存在半径问题。因为,如果我的 tempCircle 的半径 = 10 和真实圆的半径 = 9 那么我的算法找不到圆。
BitmapProcess bmpPro = new BitmapProcess((Bitmap)(pictureBox1.Image));
bmpPro.LockBits();
int black = 0, temp = 0,fixX=0,fixY=0;
Bitmap bmp = (Bitmap)pictureBox1.Image;
Graphics g = this.pictureBox1.CreateGraphics();
Pen pen = new Pen(Color.Red, 10);
int x = 0, y = 0;
for (int a = 0; a < pictureBox1.Image.Width - 1; a += 1)
{
for (int b = 0; b < pictureBox1.Image.Height - 1; b++)
{
double radius = 10;
temp = 0;
for (double i = 0.0; i < 360.0; i += 1)
{
double angle = i * System.Math.PI / 180;
x = (int)(a + radius * System.Math.Cos(angle));
y = (int)(b + radius * System.Math.Sin(angle));
Color aa = bmpPro.GetPixel(Math.Abs(x), Math.Abs(y));
if (bmpPro.GetPixel(Math.Abs(x), Math.Abs(y))!=Color.Black) temp++;
}
if (temp > black)
{
black = temp;
fixX = a;
fixY = b;
}
}
}
g.DrawEllipse(pen,fixX,fixY,50,50);
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace LockBitBitmap
{
class BitmapProcess
{
public int b = 0;
int TotalPixelLocked;
Bitmap source = null; //kaynak bmp
IntPtr Iptr = IntPtr.Zero; //baslangıc adresi
BitmapData bitmapData = null;
public byte[] Pixels { get; set; }
public int Depth { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public BitmapProcess(Bitmap source)
{
this.source = source; //bitmapı dısardan al
}
public void LockBits()
{
//resmin en ve boyunu al
Width = source.Width;
Height = source.Height;
//kilit için rectangle olustur
Rectangle rect = new Rectangle(0,0,Width,Height);
//kaynak bitmap ın pixel formatını al
Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat);
//pixel basına bit sayısını bul(Bpp)
if (Depth != 8 && Depth != 24 && Depth != 32)
{
return; //bit türü desteklenmiyor...
}
//bitmapı kilitle ve veriyi döndür...
bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite, source.PixelFormat);
//kilitlenecek pixel sayısını al
TotalPixelLocked = Math.Abs(bitmapData.Stride) * source.Height;
//pixel verilerini tutmak için byte dizisi olustur
int step = Depth / 8;
Pixels = new byte[TotalPixelLocked * step];
Iptr = bitmapData.Scan0;
//verileri pointerden diziye aktar
Marshal.Copy(Iptr, Pixels, 0, TotalPixelLocked);
}
public Color GetPixel(int x, int y)
{
Color clr = Color.Empty; //boş renk
//renkli nesne sayısını al
int cCount = Depth / 8;
//istenen pixelin baslangıc adresini bul
int i = y * bitmapData.Stride + x * cCount;
if (i > (Pixels.Length - cCount))
{
throw new IndexOutOfRangeException("index out of range ( dizi adresi geçersiz)");
}
if (Depth == 32) //r g b a
{
byte b = Pixels[i];
byte g = Pixels[i + 1];
byte r = Pixels[i + 2];
byte a = Pixels[i + 3];
clr = Color.FromArgb(a,r,g,b);
}
if (Depth == 24) //r g b
{
byte b = Pixels[i];
byte g = Pixels[i + 1];
byte r = Pixels[i + 2];
clr = Color.FromArgb(r, g, b);
}
if (Depth == 8) // r g b hepsi aynı
{
byte c = Pixels[i];
clr = Color.FromArgb(c,c,c);
}
return clr;
}
public void SetPixel(int x, int y, Color color)
{
// renkli nesne sayısı
int cCount = Depth / 8;
// baslangıc indexini bul
//int i = ((y * Width) + x) * cCount;
int i = y * bitmapData.Stride + x * cCount;
if (i > (Pixels.Length - cCount))
{
throw new IndexOutOfRangeException("index out of range ( dizi adresi geçersiz)");
}
if (Depth == 32) // r,g,b, (alpha)
{
Pixels[i] = color.B;
Pixels[i + 1] = color.G;
Pixels[i + 2] = color.R;
Pixels[i + 3] = color.A;
}
if (Depth == 24) // r,g,b
{
Pixels[i] = color.B;
Pixels[i + 1] = color.G;
Pixels[i + 2] = color.R;
b++;
}
if (Depth == 8)//r g b hepsi aynı
{
Pixels[i] = color.B;
}
}
public Bitmap giveBitmap()
{
System.Runtime.InteropServices.Marshal.Copy(Pixels, 0, Iptr, TotalPixelLocked);
source.UnlockBits(bitmapData);
return source;
}
}
}
不如看看谢永红和季强的论文"A NEW EFFICIENT ELLIPSE DETECTION METHOD"。 link
我知道它的标题中有椭圆,但你肯定知道 cricle 是非常特殊的椭圆 ;)。
提示:您处理的下一步应该是 聚类 您的边缘像素 - 这样您就会知道哪些像素正在创建 object。我们知道 object 有一些 属性 并且您应该确定哪些 object 属性使 object 成为一个圆圈 - 然后实现这些 properties-based 在你的代码中过滤。
如果图片中只有几个圆圈,我建议使用 RANSAC。
简要说明:从你的边缘像素中,随机选取 3 个点,find the equation of the circle 适合这些点。在所有剩余的边缘点上使用这个方程,看看有多少边缘点位于圆上,即有多少满足方程。重复这个过程N次(N可以根据各种因素决定,包括边缘点的数量、圆的数量等),记录inliers(位于圆上的点)数量最多的迭代。这个最大迭代对应的方程就是你的最佳拟合圆。
如果图像中有多个圆圈,请在删除或屏蔽掉所有符合先前找到的圆圈的点后重复上述步骤。