我正在尝试识别目标中的洞并相应地对其进行评分。我试图找到轮廓,它做了很多工作,但它没有给我100%的结果。有时它给我一个准确的结果,有时它会错过一些子弹。我不知道怎么做。我是开放式CV和图像处理的新手。可能是由于相机的实时流媒体和灯光频率。请帮我解决这个问题。
我目标的详细信息
目标图像
带孔的图像
灰度图像
这是我从相机获取视频的代码:
private void button1_Click(object sender, EventArgs e)
{
if (capture == null)
{
Cursor.Current = Cursors.WaitCursor;
//capture = new Capture(0);
capture = new Capture("rtsp://admin:[email protected]:554/live.avi");
capture.ImageGrabbed += Capture_ImageGrabbed;
capture.Start();
Cursor.Current = Cursors.Default;
}
index = 0;
if (index < panlist.Count)
{
panlist[++index].BringToFront();
}
CamPnelList[0].BackColor = Color.Red;
Rifle = true;
}
private void Capture_ImageGrabbed(object sender, EventArgs e)
{
try
{
Mat m = new Mat();
capture.Retrieve(m);
imginpt = m.ToImage<Gray, byte>();
RecImg = m.ToImage<Rgb, byte>();
if (rec.X != 0 && rec.Y != 0 && CamPnelList[0].BackColor == Color.LightGreen)
{
imginpt.ROI = rec;
RecImg.ROI = rec;
imgout1 = new Image<Gray, byte>(imginpt.Width, imginpt.Height, new Gray(0));
imgout1 = imginpt.Convert<Gray, byte>().ThresholdBinary(new Gray(100), new Gray(255));
imginpt.ROI = Rectangle.Empty;
tempimg1 = imgout1.CopyBlank();
imgout1.CopyTo(tempimg1);
cam1pictureBox.Image = imgout1.Bitmap;
//Application.DoEvents();
}
else
{
cam1pictureBox.Image = imginpt.Bitmap;
}
//System.Threading.Thread.Sleep(50);
}
catch (Exception x)
{
// MessageBox.Show(x.ToString());
}
}
这是我如何提取轮廓:
contoursimg1 = new Image<Gray, byte>(tempimg1.Width, tempimg1.Height, new Gray(0));
Emgu.CV.Util.VectorOfVectorOfPoint contours = new Emgu.CV.Util.VectorOfVectorOfPoint();
Mat Hier = new Mat();
CvInvoke.FindContours(tempimg1, contours, Hier, Emgu.CV.CvEnum.RetrType.Tree, Emgu.CV.CvEnum.ChainApproxMethod.ChainApproxSimple);
CvInvoke.DrawContours(contoursimg1, contours, -1, new MCvScalar(255, 0, 0));
我已经完成了一些使用视频作为源的类似项目,当目标对象很小但是定义相当明确时,我已经采用了帧之间的差异并使用了blob检测,这是一种很好的快速算法,可以在处理时使用与实时视频。我注意到你的两个示例镜头之间的视角似乎有所改变,所以我尝试了以下代码:
const int blobSizeMin = 1;
const int blobSizeMax = 5;
var white = new Bgr(255, 255, 255).MCvScalar;
Mat frame = CvInvoke.Imread(@"e:\temp\Frame.jpg", ImreadModes.Grayscale);
Mat mask = CvInvoke.Imread(@"e:\temp\Mask.jpg", ImreadModes.Grayscale);
frame.CopyTo(frame = new Mat(), mask);
CvInvoke.BitwiseNot(frame, frame);
CvInvoke.Threshold(frame, frame, 128, 255, ThresholdType.ToZero);
var blobs = new Emgu.CV.Cvb.CvBlobs();
var blobDetector = new Emgu.CV.Cvb.CvBlobDetector();
Image<Gray, Byte> img = frame.ToImage<Gray, Byte>();
blobDetector.Detect(img, blobs);
int bulletNumber = 0;
foreach (var blob in blobs.Values)
{
if (blob.BoundingBox.Width >= blobSizeMin && blob.BoundingBox.Width <= blobSizeMax
&& blob.BoundingBox.Height >= blobSizeMin && blob.BoundingBox.Height <= blobSizeMax)
{
bulletNumber++;
Point textPos = new Point((int) blob.Centroid.X - 1, (int) blob.Centroid.Y - 1);
CvInvoke.PutText(frame, bulletNumber.ToString(), textPos, FontFace.HersheyPlain,
fontScale: 1, color: white);
}
}
CvInvoke.Imwrite(@"e:\temp\Out.png", frame);
它反转框架,使孔为白色,丢弃低于50%的值,然后进行斑点检测,只注意大小在1到5像素之间的斑点。这已经接近工作了,但是在左上角和右上角以及左下角拾取了一些额外的点,看起来非常类似于眼睛的弹孔。我将过去的相机安装在一个固定的位置时所做的就是要有一个黑白掩模图像来移除感兴趣区域之外的任何东西:
Mask.jpg
一旦添加,我检测到总共21个看起来正确的弹孔:
Out.png
但假设你实时检测到镜头,我认为你应该好好看看帧之间的差异,这应该不需要使用掩模图像。看一下CvInvoke.Subtract
方法,从一些现有代码中可以使用如下代码:
CvInvoke.Subtract(frame, lastFrame, diff);
CvInvoke.CvtColor(diff, gray, ColorConversion.Bgr2Gray);
CvInvoke.Threshold(gray, gray, detectThreshold, 255, ThresholdType.ToZero);