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

//----------------------------------------------------------------------------
// 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 &lt; hue &lt; 160 AND saturation &gt; 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;
}
}
}
}