mirror of https://github.com/emgucv/emgucv.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
220 lines
8.5 KiB
220 lines
8.5 KiB
//----------------------------------------------------------------------------
|
|
// Copyright (C) 2004-2015 by EMGU Corporation. All rights reserved.
|
|
//----------------------------------------------------------------------------
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Configuration;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.Text;
|
|
using Emgu.CV;
|
|
using Emgu.CV.CvEnum;
|
|
using Emgu.CV.Features2D;
|
|
using Emgu.CV.Structure;
|
|
using Emgu.CV.Util;
|
|
using Emgu.Util;
|
|
using Emgu.CV.XFeatures2D;
|
|
|
|
namespace TrafficSignRecognition
|
|
{
|
|
public class StopSignDetector : DisposableObject
|
|
{
|
|
private VectorOfKeyPoint _modelKeypoints;
|
|
private Mat _modelDescriptors;
|
|
private BFMatcher _modelDescriptorMatcher;
|
|
//private Features2DTracker<float> _tracker;
|
|
private SURF _detector;
|
|
|
|
private VectorOfPoint _octagon;
|
|
|
|
public StopSignDetector(IInputArray stopSignModel)
|
|
{
|
|
_detector = new SURF(500);
|
|
using (Mat redMask = new Mat())
|
|
{
|
|
GetRedPixelMask(stopSignModel, redMask);
|
|
_modelKeypoints = new VectorOfKeyPoint();
|
|
_modelDescriptors = new Mat();
|
|
_detector.DetectAndCompute(redMask, null, _modelKeypoints, _modelDescriptors, false);
|
|
if (_modelKeypoints.Size == 0)
|
|
throw new Exception("No image feature has been found in the stop sign model");
|
|
}
|
|
|
|
_modelDescriptorMatcher = new BFMatcher(DistanceType.L2);
|
|
_modelDescriptorMatcher.Add(_modelDescriptors);
|
|
|
|
_octagon = new VectorOfPoint(
|
|
new Point[]
|
|
{
|
|
new Point(1, 0),
|
|
new Point(2, 0),
|
|
new Point(3, 1),
|
|
new Point(3, 2),
|
|
new Point(2, 3),
|
|
new Point(1, 3),
|
|
new Point(0, 2),
|
|
new Point(0, 1)
|
|
});
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compute the red pixel mask for the given image.
|
|
/// A red pixel is a pixel where: 20 < hue < 160 AND saturation > 10
|
|
/// </summary>
|
|
/// <param name="image">The color image to find red mask from</param>
|
|
/// <param name="mask">The red pixel mask</param>
|
|
private static void GetRedPixelMask(IInputArray image, IInputOutputArray mask)
|
|
{
|
|
bool useUMat;
|
|
using (InputOutputArray ia = mask.GetInputOutputArray())
|
|
useUMat = ia.IsUMat;
|
|
|
|
using (IImage hsv = useUMat ? (IImage)new UMat() : (IImage)new Mat())
|
|
using (IImage s = useUMat ? (IImage)new UMat() : (IImage)new Mat())
|
|
{
|
|
CvInvoke.CvtColor(image, hsv, ColorConversion.Bgr2Hsv);
|
|
CvInvoke.ExtractChannel(hsv, mask, 0);
|
|
CvInvoke.ExtractChannel(hsv, s, 1);
|
|
|
|
//the mask for hue less than 20 or larger than 160
|
|
using (ScalarArray lower = new ScalarArray(20))
|
|
using (ScalarArray upper = new ScalarArray(160))
|
|
CvInvoke.InRange(mask, lower, upper, mask);
|
|
CvInvoke.BitwiseNot(mask, mask);
|
|
|
|
//s is the mask for saturation of at least 10, this is mainly used to filter out white pixels
|
|
CvInvoke.Threshold(s, s, 10, 255, ThresholdType.Binary);
|
|
CvInvoke.BitwiseAnd(mask, s, mask, null);
|
|
|
|
}
|
|
}
|
|
|
|
private void FindStopSign(Mat img, List<Mat> stopSignList, List<Rectangle> boxList, VectorOfVectorOfPoint contours, int[,] hierachy, int idx)
|
|
{
|
|
for (; idx >= 0; idx = hierachy[idx, 0])
|
|
{
|
|
using (VectorOfPoint c = contours[idx])
|
|
using (VectorOfPoint approx = new VectorOfPoint())
|
|
{
|
|
CvInvoke.ApproxPolyDP(c, approx, CvInvoke.ArcLength(c, true) * 0.02, true);
|
|
double area = CvInvoke.ContourArea(approx);
|
|
if (area > 200)
|
|
{
|
|
double ratio = CvInvoke.MatchShapes(_octagon, approx, Emgu.CV.CvEnum.ContoursMatchType.I3);
|
|
|
|
if (ratio > 0.1) //not a good match of contour shape
|
|
{
|
|
//check children
|
|
if (hierachy[idx, 2] >= 0)
|
|
FindStopSign(img, stopSignList, boxList, contours, hierachy, hierachy[idx, 2]);
|
|
continue;
|
|
}
|
|
|
|
Rectangle box = CvInvoke.BoundingRectangle(c);
|
|
|
|
Mat candidate = new Mat();
|
|
using (Mat tmp = new Mat(img, box))
|
|
CvInvoke.CvtColor(tmp, candidate, ColorConversion.Bgr2Gray);
|
|
|
|
//set the value of pixels not in the contour region to zero
|
|
using (Mat mask = new Mat(candidate.Size.Height, candidate.Width, DepthType.Cv8U, 1))
|
|
{
|
|
mask.SetTo(new MCvScalar(0));
|
|
CvInvoke.DrawContours(mask, contours, idx, new MCvScalar(255), -1, LineType.EightConnected, null, int.MaxValue, new Point(-box.X, -box.Y));
|
|
|
|
double mean = CvInvoke.Mean(candidate, mask).V0;
|
|
CvInvoke.Threshold(candidate, candidate, mean, 255, ThresholdType.Binary);
|
|
CvInvoke.BitwiseNot(candidate, candidate);
|
|
CvInvoke.BitwiseNot(mask, mask);
|
|
|
|
candidate.SetTo(new MCvScalar(0), mask);
|
|
}
|
|
|
|
int minMatchCount = 8;
|
|
double uniquenessThreshold = 0.8;
|
|
VectorOfKeyPoint _observeredKeypoint = new VectorOfKeyPoint();
|
|
Mat _observeredDescriptor = new Mat();
|
|
_detector.DetectAndCompute(candidate, null, _observeredKeypoint, _observeredDescriptor, false);
|
|
|
|
if (_observeredKeypoint.Size >= minMatchCount)
|
|
{
|
|
int k = 2;
|
|
|
|
Mat mask;
|
|
|
|
using (VectorOfVectorOfDMatch matches = new VectorOfVectorOfDMatch())
|
|
{
|
|
_modelDescriptorMatcher.KnnMatch(_observeredDescriptor, matches, k, null);
|
|
mask = new Mat(matches.Size, 1, DepthType.Cv8U, 1);
|
|
mask.SetTo(new MCvScalar(255));
|
|
Features2DToolbox.VoteForUniqueness(matches, uniquenessThreshold, mask);
|
|
}
|
|
|
|
int nonZeroCount = CvInvoke.CountNonZero(mask);
|
|
if (nonZeroCount >= minMatchCount)
|
|
{
|
|
boxList.Add(box);
|
|
stopSignList.Add(candidate);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void DetectStopSign(Mat img, List<Mat> stopSignList, List<Rectangle> boxList)
|
|
{
|
|
Mat smoothImg = new Mat();
|
|
CvInvoke.GaussianBlur(img, smoothImg, new Size(5, 5), 1.5, 1.5);
|
|
//Image<Bgr, Byte> smoothImg = img.SmoothGaussian(5, 5, 1.5, 1.5);
|
|
|
|
Mat smoothedRedMask = new Mat();
|
|
GetRedPixelMask(smoothImg, smoothedRedMask);
|
|
|
|
//Use Dilate followed by Erode to eliminate small gaps in some contour.
|
|
CvInvoke.Dilate(smoothedRedMask, smoothedRedMask, null, new Point(-1, -1), 1, BorderType.Constant, CvInvoke.MorphologyDefaultBorderValue);
|
|
CvInvoke.Erode(smoothedRedMask, smoothedRedMask, null, new Point(-1, -1), 1, BorderType.Constant, CvInvoke.MorphologyDefaultBorderValue);
|
|
|
|
using (Mat canny = new Mat())
|
|
using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
|
|
{
|
|
CvInvoke.Canny(smoothedRedMask, canny, 100, 50);
|
|
int[,] hierachy = CvInvoke.FindContourTree(canny, contours, ChainApproxMethod.ChainApproxSimple);
|
|
|
|
//Image<Gray, Byte> tmp = new Image<Gray, byte>(canny.Size);
|
|
//CvInvoke.DrawContours(tmp, contours, -1, new MCvScalar(255, 255, 255));
|
|
//Emgu.CV.UI.ImageViewer.Show(tmp);
|
|
|
|
if (hierachy.GetLength(0) > 0)
|
|
FindStopSign(img, stopSignList, boxList, contours, hierachy, 0);
|
|
}
|
|
|
|
}
|
|
|
|
protected override void DisposeObject()
|
|
{
|
|
if (_modelKeypoints != null)
|
|
{
|
|
_modelKeypoints.Dispose();
|
|
_modelKeypoints = null;
|
|
}
|
|
if (_modelDescriptors != null)
|
|
{
|
|
_modelDescriptors.Dispose();
|
|
_modelDescriptors = null;
|
|
}
|
|
if (_modelDescriptorMatcher != null)
|
|
{
|
|
_modelDescriptorMatcher.Dispose();
|
|
_modelDescriptorMatcher = null;
|
|
}
|
|
if (_octagon != null)
|
|
{
|
|
_octagon.Dispose();
|
|
_octagon = null;
|
|
}
|
|
}
|
|
}
|
|
}
|