123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652 |
- namespace VisualMath.Accord.Imaging
- {
- using System.Collections.Generic;
- using System.Drawing;
- using System.Drawing.Imaging;
- using AForge.Imaging;
- using AForge.Imaging.Filters;
- using AForge;
- /// <summary>
- /// Harris Corners Detector.
- /// </summary>
- /// <remarks>
- /// <para>This class implements the Harris corners detector.</para>
- /// <para>Sample usage:</para>
- /// <code>
- /// // create corners detector's instance
- /// HarrisCornersDetector hcd = new HarrisCornersDetector( );
- /// // process image searching for corners
- /// Point[] corners = hcd.ProcessImage( image );
- /// // process points
- /// foreach ( Point corner in corners )
- /// {
- /// // ...
- /// }
- /// </code>
- ///
- /// <para>
- /// References:
- /// <list type="bullet">
- /// <item><description>
- /// P. D. Kovesi. MATLAB and Octave Functions for Computer Vision and Image Processing.
- /// School of Computer Science and Software Engineering, The University of Western Australia.
- /// Available in: http://www.csse.uwa.edu.au/~pk/Research/MatlabFns/Spatial/harris.m</description></item>
- /// <item><description>
- /// C.G. Harris and M.J. Stephens. "A combined corner and edge detector",
- /// Proceedings Fourth Alvey Vision Conference, Manchester.
- /// pp 147-151, 1988.</description></item>
- /// <item><description>
- /// Alison Noble, "Descriptions of Image Surfaces", PhD thesis, Department
- /// of Engineering Science, Oxford University 1989, p45.</description></item>
- /// </list>
- /// </para>
- /// </remarks>
- ///
- /// <seealso cref="MoravecCornersDetector"/>
- /// <seealso cref="SusanCornersDetector"/>
- ///
- public class HarrisCornersDetector : ICornersDetector
- {
- private float k = 0.04f;
- private float threshold = 1000f;
- private double sigma = 1.4;
- private int r = 3;
- /// <summary>
- /// Harris parameter k. Default value is 0.04.
- /// </summary>
- public float K
- {
- get { return k; }
- set { k = value; }
- }
- /// <summary>
- /// Harris threshold. Default value is 1000.
- /// </summary>
- public float Threshold
- {
- get { return threshold; }
- set { threshold = value; }
- }
- /// <summary>
- /// Gaussian smoothing sigma. Default value is 1.4.
- /// </summary>
- public double Sigma
- {
- get { return sigma; }
- set { sigma = value; }
- }
- /// <summary>
- /// Non-maximum suppression window radius. Default value is 3.
- /// </summary>
- public int Suppression
- {
- get { return r; }
- set { r = value; }
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="HarrisCornersDetector"/> class.
- /// </summary>
- public HarrisCornersDetector()
- {
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="HarrisCornersDetector"/> class.
- /// </summary>
- public HarrisCornersDetector(float k)
- : this()
- {
- this.k = k;
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="HarrisCornersDetector"/> class.
- /// </summary>
- public HarrisCornersDetector(float k, float threshold)
- : this()
- {
- this.k = k;
- this.threshold = threshold;
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="HarrisCornersDetector"/> class.
- /// </summary>
- public HarrisCornersDetector(float k, float threshold, double sigma)
- : this()
- {
- this.k = k;
- this.threshold = threshold;
- this.sigma = sigma;
- }
- /// <summary>
- /// Process image looking for corners.
- /// </summary>
- ///
- /// <param name="image">Source image data to process.</param>
- ///
- /// <returns>Returns list of found corners (X-Y coordinates).</returns>
- ///
- /// <exception cref="UnsupportedImageFormatException">
- /// The source image has incorrect pixel format.
- /// </exception>
- ///
- public List<IntPoint> ProcessImage(UnmanagedImage image)
- {
- // check image format
- if (
- (image.PixelFormat != PixelFormat.Format8bppIndexed) &&
- (image.PixelFormat != PixelFormat.Format24bppRgb) &&
- (image.PixelFormat != PixelFormat.Format32bppRgb) &&
- (image.PixelFormat != PixelFormat.Format32bppArgb)
- )
- {
- throw new UnsupportedImageFormatException("Unsupported pixel format of the source image.");
- }
- // make sure we have grayscale image
- UnmanagedImage grayImage = null;
- if (image.PixelFormat == PixelFormat.Format8bppIndexed)
- {
- grayImage = image;
- }
- else
- {
- // create temporary grayscale image
- grayImage = Grayscale.CommonAlgorithms.BT709.Apply(image);
- }
- // get source image size
- int width = grayImage.Width;
- int height = grayImage.Height;
- int stride = grayImage.Stride;
- int offset = stride - width;
- // 1. Calculate partial differences
- UnmanagedImage diffx = UnmanagedImage.Create(width, height, PixelFormat.Format8bppIndexed);
- UnmanagedImage diffy = UnmanagedImage.Create(width, height, PixelFormat.Format8bppIndexed);
- UnmanagedImage diffxy = UnmanagedImage.Create(width, height, PixelFormat.Format8bppIndexed);
- unsafe
- {
- // Compute dx and dy
- byte* src = (byte*)grayImage.ImageData.ToPointer();
- byte* dx = (byte*)diffx.ImageData.ToPointer();
- byte* dy = (byte*)diffy.ImageData.ToPointer();
- byte* dxy = (byte*)diffxy.ImageData.ToPointer();
- // for each line
- for (int y = 0; y < height; y++)
- {
- // for each pixel
- for (int x = 0; x < width; x++, src++, dx++, dy++)
- {
- // TODO: Place those verifications
- // outside the innermost loop
- if (x == 0 || x == width - 1 ||
- y == 0 || y == height - 1)
- {
- *dx = *dy = 0; continue;
- }
- int h = -(src[-stride - 1] + src[-1] + src[stride - 1]) +
- (src[-stride + 1] + src[+1] + src[stride + 1]);
- *dx = (byte)(h > 255 ? 255 : h < 0 ? 0 : h);
- int v = -(src[-stride - 1] + src[-stride] + src[-stride + 1]) +
- (src[+stride - 1] + src[+stride] + src[+stride + 1]);
- *dy = (byte)(v > 255 ? 255 : v < 0 ? 0 : v);
- }
- src += offset;
- dx += offset;
- dy += offset;
- }
- // Compute dxy
- dx = (byte*)diffx.ImageData.ToPointer();
- dxy = (byte*)diffxy.ImageData.ToPointer();
- // for each line
- for (int y = 0; y < height; y++)
- {
- // for each pixel
- for (int x = 0; x < width; x++, dx++, dxy++)
- {
- if (x == 0 || x == width - 1 ||
- y == 0 || y == height - 1)
- {
- *dxy = 0; continue;
- }
- int v = -(dx[-stride - 1] + dx[-stride] + dx[-stride + 1]) +
- (dx[+stride - 1] + dx[+stride] + dx[+stride + 1]);
- *dxy = (byte)(v > 255 ? 255 : v < 0 ? 0 : v);
- }
- dx += offset;
- dxy += offset;
- }
- }
- // 2. Smooth the diff images
- if (sigma > 0.0)
- {
- GaussianBlur blur = new GaussianBlur(sigma);
- blur.ApplyInPlace(diffx);
- blur.ApplyInPlace(diffy);
- blur.ApplyInPlace(diffxy);
- }
- // 3. Compute Harris Corner Response
- float[,] H = new float[height, width];
- unsafe
- {
- byte* ptrA = (byte*)diffx.ImageData.ToPointer();
- byte* ptrB = (byte*)diffy.ImageData.ToPointer();
- byte* ptrC = (byte*)diffxy.ImageData.ToPointer();
- float M, A, B, C;
- for (int y = 0; y < height; y++)
- {
- for (int x = 0; x < width; x++)
- {
- A = *(ptrA++);
- B = *(ptrB++);
- C = *(ptrC++);
- // Harris corner measure
- M = (A * B - C * C) - (k * ((A + B) * (A + B)));
- if (M > threshold)
- H[y, x] = M;
- else H[y, x] = 0;
- }
- ptrA += offset;
- ptrB += offset;
- ptrC += offset;
- }
- }
- // Free resources
- diffx.Dispose();
- diffy.Dispose();
- diffxy.Dispose();
- if (image.PixelFormat != PixelFormat.Format8bppIndexed)
- grayImage.Dispose();
- // 4. Suppress non-maximum points
- List<IntPoint> cornersList = new List<IntPoint>();
- // for each row
- for (int y = r, maxY = height - r; y < maxY; y++)
- {
- // for each pixel
- for (int x = r, maxX = width - r; x < maxX; x++)
- {
- float currentValue = H[y, x];
- // for each windows' row
- for (int i = -r; (currentValue != 0) && (i <= r); i++)
- {
- // for each windows' pixel
- for (int j = -r; j <= r; j++)
- {
- if (H[y + i, x + j] > currentValue)
- {
- currentValue = 0;
- break;
- }
- }
- }
- // check if this point is really interesting
- if (currentValue != 0)
- {
- cornersList.Add(new IntPoint(x, y));
- }
- }
- }
- return cornersList;
- }
- /// <summary>
- /// Process image looking for corners.
- /// </summary>
- ///
- /// <param name="imageData">Source image data to process.</param>
- ///
- /// <returns>Returns list of found corners (X-Y coordinates).</returns>
- ///
- /// <exception cref="UnsupportedImageFormatException">
- /// The source image has incorrect pixel format.
- /// </exception>
- ///
- public List<IntPoint> ProcessImage(BitmapData imageData)
- {
- return ProcessImage(new UnmanagedImage(imageData));
- }
- /// <summary>
- /// Process image looking for corners.
- /// </summary>
- ///
- /// <param name="image">Source image data to process.</param>
- ///
- /// <returns>Returns list of found corners (X-Y coordinates).</returns>
- ///
- /// <exception cref="UnsupportedImageFormatException">
- /// The source image has incorrect pixel format.
- /// </exception>
- ///
- public List<IntPoint> ProcessImage(Bitmap image)
- {
- // check image format
- if (
- (image.PixelFormat != PixelFormat.Format8bppIndexed) &&
- (image.PixelFormat != PixelFormat.Format24bppRgb) &&
- (image.PixelFormat != PixelFormat.Format32bppRgb) &&
- (image.PixelFormat != PixelFormat.Format32bppArgb)
- )
- {
- throw new UnsupportedImageFormatException("Unsupported pixel format of the source");
- }
- // lock source image
- BitmapData imageData = image.LockBits(
- new Rectangle(0, 0, image.Width, image.Height),
- ImageLockMode.ReadOnly, image.PixelFormat);
- List<IntPoint> corners;
- try
- {
- // process the image
- corners = ProcessImage(new UnmanagedImage(imageData));
- }
- finally
- {
- // unlock image
- image.UnlockBits(imageData);
- }
- return corners;
- }
- /// <summary>
- /// Process image looking for corners.
- /// </summary>
- ///
- /// <param name="image">Source image data to process.</param>
- ///
- /// <returns>Returns list of found corners (X-Y coordinates).</returns>
- ///
- /// <exception cref="UnsupportedImageFormatException">
- /// The source image has incorrect pixel format.
- /// </exception>
- ///
- public List<Point> ProcessImageMethod(Bitmap image)
- {
- // check image format
- if (
- (image.PixelFormat != PixelFormat.Format8bppIndexed) &&
- (image.PixelFormat != PixelFormat.Format24bppRgb) &&
- (image.PixelFormat != PixelFormat.Format32bppRgb) &&
- (image.PixelFormat != PixelFormat.Format32bppArgb)
- )
- {
- throw new UnsupportedImageFormatException("Unsupported pixel format of the source");
- }
- // lock source image
- BitmapData imageData = image.LockBits(
- new Rectangle(0, 0, image.Width, image.Height),
- ImageLockMode.ReadOnly, image.PixelFormat);
- List<Point> corners;// = new List<Point>();
- try
- {
- // process the image
- corners = ProcessImageMethod(new UnmanagedImage(imageData));
- }
- finally
- {
- // unlock image
- image.UnlockBits(imageData);
- }
- return corners;
- }
- /// <summary>
- /// Process image looking for corners.
- /// </summary>
- ///
- /// <param name="image">Source image data to process.</param>
- ///
- /// <returns>Returns list of found corners (X-Y coordinates).</returns>
- ///
- /// <exception cref="UnsupportedImageFormatException">
- /// The source image has incorrect pixel format.
- /// </exception>
- ///
- public List<Point> ProcessImageMethod(UnmanagedImage image)
- {
- // check image format
- if (
- (image.PixelFormat != PixelFormat.Format8bppIndexed) &&
- (image.PixelFormat != PixelFormat.Format24bppRgb) &&
- (image.PixelFormat != PixelFormat.Format32bppRgb) &&
- (image.PixelFormat != PixelFormat.Format32bppArgb)
- )
- {
- throw new UnsupportedImageFormatException("Unsupported pixel format of the source image.");
- }
- // make sure we have grayscale image
- UnmanagedImage grayImage = null;
- if (image.PixelFormat == PixelFormat.Format8bppIndexed)
- {
- grayImage = image;
- }
- else
- {
- // create temporary grayscale image
- grayImage = Grayscale.CommonAlgorithms.BT709.Apply(image);
- }
- // get source image size
- int width = grayImage.Width;
- int height = grayImage.Height;
- int stride = grayImage.Stride;
- int offset = stride - width;
- // 1. Calculate partial differences
- UnmanagedImage diffx = UnmanagedImage.Create(width, height, PixelFormat.Format8bppIndexed);
- UnmanagedImage diffy = UnmanagedImage.Create(width, height, PixelFormat.Format8bppIndexed);
- UnmanagedImage diffxy = UnmanagedImage.Create(width, height, PixelFormat.Format8bppIndexed);
- unsafe
- {
- // Compute dx and dy
- byte* src = (byte*)grayImage.ImageData.ToPointer();
- byte* dx = (byte*)diffx.ImageData.ToPointer();
- byte* dy = (byte*)diffy.ImageData.ToPointer();
- byte* dxy = (byte*)diffxy.ImageData.ToPointer();
- // for each line
- for (int y = 0; y < height; y++)
- {
- // for each pixel
- for (int x = 0; x < width; x++, src++, dx++, dy++)
- {
- // TODO: Place those verifications
- // outside the innermost loop
- if (x == 0 || x == width - 1 ||
- y == 0 || y == height - 1)
- {
- *dx = *dy = 0; continue;
- }
- int h = -(src[-stride - 1] + src[-1] + src[stride - 1]) +
- (src[-stride + 1] + src[+1] + src[stride + 1]);
- *dx = (byte)(h > 255 ? 255 : h < 0 ? 0 : h);
- int v = -(src[-stride - 1] + src[-stride] + src[-stride + 1]) +
- (src[+stride - 1] + src[+stride] + src[+stride + 1]);
- *dy = (byte)(v > 255 ? 255 : v < 0 ? 0 : v);
- }
- src += offset;
- dx += offset;
- dy += offset;
- }
- // Compute dxy
- dx = (byte*)diffx.ImageData.ToPointer();
- dxy = (byte*)diffxy.ImageData.ToPointer();
- // for each line
- for (int y = 0; y < height; y++)
- {
- // for each pixel
- for (int x = 0; x < width; x++, dx++, dxy++)
- {
- if (x == 0 || x == width - 1 ||
- y == 0 || y == height - 1)
- {
- *dxy = 0; continue;
- }
- int v = -(dx[-stride - 1] + dx[-stride] + dx[-stride + 1]) +
- (dx[+stride - 1] + dx[+stride] + dx[+stride + 1]);
- *dxy = (byte)(v > 255 ? 255 : v < 0 ? 0 : v);
- }
- dx += offset;
- dxy += offset;
- }
- }
- // 2. Smooth the diff images
- if (sigma > 0.0)
- {
- GaussianBlur blur = new GaussianBlur(sigma);
- blur.ApplyInPlace(diffx);
- blur.ApplyInPlace(diffy);
- blur.ApplyInPlace(diffxy);
- }
- // 3. Compute Harris Corner Response
- float[,] H = new float[height, width];
- unsafe
- {
- byte* ptrA = (byte*)diffx.ImageData.ToPointer();
- byte* ptrB = (byte*)diffy.ImageData.ToPointer();
- byte* ptrC = (byte*)diffxy.ImageData.ToPointer();
- float M, A, B, C;
- for (int y = 0; y < height; y++)
- {
- for (int x = 0; x < width; x++)
- {
- A = *(ptrA++);
- B = *(ptrB++);
- C = *(ptrC++);
- // Harris corner measure
- M = (A * B - C * C) - (k * ((A + B) * (A + B)));
- if (M > threshold)
- H[y, x] = M;
- else H[y, x] = 0;
- }
- ptrA += offset;
- ptrB += offset;
- ptrC += offset;
- }
- }
- // Free resources
- diffx.Dispose();
- diffy.Dispose();
- diffxy.Dispose();
- if (image.PixelFormat != PixelFormat.Format8bppIndexed)
- grayImage.Dispose();
- // 4. Suppress non-maximum points
- List<Point> cornersList = new List<Point>();
- // for each row
- for (int y = r, maxY = height - r; y < maxY; y++)
- {
- // for each pixel
- for (int x = r, maxX = width - r; x < maxX; x++)
- {
- float currentValue = H[y, x];
- // for each windows' row
- for (int i = -r; (currentValue != 0) && (i <= r); i++)
- {
- // for each windows' pixel
- for (int j = -r; j <= r; j++)
- {
- if (H[y + i, x + j] > currentValue)
- {
- currentValue = 0;
- break;
- }
- }
- }
- // check if this point is really interesting
- if (currentValue != 0)
- {
- cornersList.Add(new Point(x, y));
- }
- }
- }
- return cornersList;
- }
- }
- }
|