# مهندسی نرم افزار > مباحث مرتبط با مهندسی نرم‌افزار > پردازش تصویر (Image Processing) >  تشخیص دایره در تصویر با استفاده از تبدیل هاف دایروی

## asefy2008

سلام دوستان
می خواستم بدونم کسی کلاس تشخیص دایره که از تبدیل هاف دایروی برای تشخیص دایره استفاده کرده باشه داره؟
(نکته : حتی الامکان با سی شارپ باشه)

در ضمن می خواستم بدونم دوستان راه بهتری دارند؟

----------


## مصطفی ساتکی

سورسش موجوده ولي تجربه ميگه كه hough circle يك روش robust براي localize كردن دايره در صفحه نيست هم بار محاسباتي بالايي داره و هم اينكه دقت مناسب رو نداره و همچنين به ميران دايره بودن blob هدف نيز خيلي حساسه.
راه بهتر اينه كه لبه ها رو در تصوير پيدا كنيد  و سپس نقاط رو با distance مشخصي نمونه برداري كنيد سپس هر سه نقطه مجاور با distance موردنظر عمود منصف رو بدست بياريد سپس نقاط تقاطع اين عمود منصف ها رو در يك accumulator انباشت كنيد مجموعه نقاط ماكزيمم در اين accumulator مجموعه اي از مراكز دايره ها موجود در صفحه هستند حالا براي اينكه fake maximum رو fade كنيد بايستي روي كل accumulator گوسين رو اعمال كنيد حالا همه نقاط صحت دارند.
حالا از اين نقاطي كه بدست آمده شما بايستي به دنبال دايره هايي با شعاع مشخص باشيد  از نقاط بدست آمده به صورت radial كليه نقاط مربوط به صفحه نمونه برداري رو كه دربازه minimum radius و maximum radius قرار دارند رو نگه داريد و بقيه نقاط رو پاك كنيد سپس به ازاي هر نقطه كليه نقاطي رو كه در عمليات فيلتر مرحله قبل  رو در يك آرايه بريزيد و به صورت سيگنال باهاش رفتار كنيد حال دوباره روي اين نقاط median رو اعمال كنيد و ماكزيمم رو در اين سيگنال بدست بياريد اين maximum ميشه شعاع مدنظر شما.(نكته : چون شعاع هاي بزرگتر تعداد sample هاشون بيشتر هميشه ديگر شعاع رو محو مي كنند بهترين راه اينه كه اين شعاع ها رو با توجه به degree شون quantize كنيد و شعاعي انتخاب شه كه بتونه بيشتر 2 پي رو پوشش بده)


namespace AForge.Imaging
{
    using System;
    using System.Collections;
    using System.Drawing;
    using System.Drawing.Imaging;

    /// <summary>
    /// Hough circle.
    /// </summary>
    /// 
    /// <remarks>Represents circle of Hough transform.</remarks>
    /// 
    public class HoughCircle : IComparable
    {
        /// <summary>
        /// Circle center's X coordinate.
        /// </summary>
        public readonly int X;

        /// <summary>
        /// Circle center's Y coordinate.
        /// </summary>
        public readonly int Y;

        /// <summary>
        /// Circle's radius.
        /// </summary>
        public readonly int Radius;

        /// <summary>
        /// Line's absolute intensity.
        /// </summary>
        public readonly short Intensity;

        /// <summary>
        /// Line's relative intensity.
        /// </summary>
        public readonly double RelativeIntensity;

        /// <summary>
        /// Initializes a new instance of the <see cref="HoughCircle"/> class.
        /// </summary>
        /// 
        /// <param name="x">Circle's X coordinate.</param>
        /// <param name="y">Circle's Y coordinate.</param>
        /// <param name="radius">Circle's radius.</param>
        /// <param name="intensity">Circle's absolute intensity.</param>
        /// <param name="relativeIntensity">Circle's relative intensity.</param>
        /// 
        public HoughCircle( int x, int y, int radius, short intensity, double relativeIntensity )
        {
            X = x;
            Y = y;
            Radius = radius;
            Intensity = intensity;
            RelativeIntensity = relativeIntensity;
        }

        /// <summary>
        /// Compare the object with another instance of this class.
        /// </summary>
        /// 
        /// <param name="value">Object to compare with.</param>
        /// 
        /// <returns><para>A signed number indicating the relative values of this instance and <b>value</b>: 1) greater than zero - 
        /// this instance is greater than <b>value</b>; 2) zero - this instance is equal to <b>value</b>;
        /// 3) greater than zero - this instance is less than <b>value</b>.</para>
        /// <para><note>The sort order is descending.</note></para></returns>
        /// 
        public int CompareTo( object value )
        {
            return ( -Intensity.CompareTo( ( (HoughCircle) value ).Intensity ) );
        }
    }

    /// <summary>
    /// Hough circle transformation.
    /// </summary>
    ///
    /// <remarks><para>Hough circle transformation allows to detect circles in image.</para>
    /// <para>Sample usage:</para>
    /// <code>
    /// HoughCircleTransformation circleTransform = new HoughCircleTransformation( );
    /// // apply Hough circle transform
    /// circleTransform.ProcessImage( sourceImage );
    /// Bitmap houghCirlceImage = circleTransform.ToBitmap( );
    /// // get circles using relative intensity
    /// HoughCircle[] circles = circleTransform.GetCirclesByRelativeIntensity( 0.5 );
    /// 
    /// foreach ( HoughCircle circle in circles )
    /// {
    ///     // ..
    /// }
    /// 
    /// </code>
    /// </remarks>
    /// 
    public class HoughCircleTransformation
    {
        // circle radius to detect
        private int radiusToDetect;

        // Hough map
        private short[,] houghMap;
        private short maxMapIntensity = 0;

        // Hough map's width and height
        private int width;
        private int height;

        private int localPeakRadius = 4;
        private short minCircleIntensity = 10;
        private ArrayList circles = new ArrayList( );

        /// <summary>
        /// Minimum circles's intensity in Hough map to recognize a circle.
        /// </summary>
        ///
        /// <remarks><para>The value sets minimum intensity level for a circle. If a value in Hough
        /// map has lower intensity, then it is not treated as a circle.</para>
        /// <para>Default value is <b>10</b>.</para></remarks>
        ///
        public short MinCircleIntensity
        {
            get { return minCircleIntensity; }
            set { minCircleIntensity = value; }
        }

        /// <summary>
        /// Radius for searching local peak value.
        /// </summary>
        /// 
        /// <remarks><para>The value determines radius around a map's value, which is analyzed to determine
        /// if the map's value is a maximum in specified area.</para>
        /// <para>Default value is <b>4</b>. Minimum value is <b>1</b>. Maximum value is <b>10</b>.</para></remarks>
        /// 
        public int LocalPeakRadius
        {
            get { return localPeakRadius; }
            set { localPeakRadius = Math.Max( 1, Math.Min( 10, value ) ); }
        }

        /// <summary>
        /// Maximum found intensity in Hough map.
        /// </summary>
        /// 
        public short MaxIntensity
        {
            get { return maxMapIntensity; }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HoughCircleTransformation"/> class.
        /// </summary>
        /// 
        public HoughCircleTransformation( int radiusToDetect )
        {
            this.radiusToDetect = radiusToDetect;
        }

        /// <summary>
        /// Found circles count.
        /// </summary>
        /// 
        public int CirclesCount
        {
            get { return circles.Count; }
        }

        /// <summary>
        /// Process an image building Hough map.
        /// </summary>
        /// 
        /// <param name="image">Source image to process.</param>
        /// 
        public void ProcessImage( Bitmap image )
        {
            // check image format
            if ( image.PixelFormat != PixelFormat.Format8bppIndexed )
                throw new ArgumentException( "Pixel format of source image should be 8 bpp indexed" );

            // lock source image
            BitmapData imageData = image.LockBits(
                new Rectangle( 0, 0, image.Width, image.Height ),
                ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed );

            // process the image
            ProcessImage( imageData );

            // unlock image
            image.UnlockBits( imageData );
        }

        /// <summary>
        /// Process an image building Hough map.
        /// </summary>
        /// 
        /// <param name="imageData">Source image data to process.</param>
        /// 
        public void ProcessImage( BitmapData imageData )
        {
            if ( imageData.PixelFormat != PixelFormat.Format8bppIndexed )
                throw new ArgumentException( "Pixel format of source image should be 8 bpp indexed" );

            // get source image size
            width   = imageData.Width;
            height  = imageData.Height;

            int srcOffset = imageData.Stride - width;

            // allocate Hough map of the same size like image
            houghMap = new short[height, width];

            // do the job
            unsafe
            {
                byte* src = (byte*) imageData.Scan0.ToPointer( );

                // for each row
                for ( int y = 0; y < height; y++ )
                {
                    // for each pixel
                    for ( int x = 0; x < width; x++, srC++‎ )
                    {
                        if ( *src != 0 )
                        {
                            DrawHoughCircle( x, y );
                        }
                    }
                    src += srcOffset;
                }
            }

            // find max value in Hough map
            maxMapIntensity = 0;
            for ( int i = 0; i < height; i++ )
            {
                for ( int j = 0; j < width; j++ )
                {
                    if ( houghMap[i, j] > maxMapIntensity )
                    {
                        maxMapIntensity = houghMap[i, j];
                    }
                }
            }

            CollectCircles( );
        }

        /// <summary>
        /// onvert Hough map to bitmap. 
        /// </summary>
        /// 
        /// <returns>Returns a bitmap, which shows Hough map.</returns>
        /// 
        public Bitmap ToBitmap( )
        {
            // check if Hough transformation was made already
            if ( houghMap == null )
            {
                throw new ApplicationException( "Hough transformation was not done yet" );
            }

            int width = houghMap.GetLength( 1 );
            int height = houghMap.GetLength( 0 );

            // create new image
            Bitmap image = AForge.Imaging.Image.CreateGrayscaleImage( width, height );

            // lock destination bitmap data
            BitmapData imageData = image.LockBits(
                new Rectangle( 0, 0, width, height ),
                ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed );

            int offset = imageData.Stride - width;
            float scale = 255.0f / maxMapIntensity;

            // do the job
            unsafe
            {
                byte* dst = (byte*) imageData.Scan0.ToPointer( );

                for ( int y = 0; y < height; y++ )
                {
                    for ( int x = 0; x < width; x++, dst++ )
                    {
                        *dst = (byte) System.Math.Min( 255, (int) ( scale * houghMap[y, x] ) );
                    }
                    dst += offset;
                }
            }

            // unlock destination images
            image.UnlockBits( imageData );

            return image;
        }

        /// <summary>
        /// Get specified amount of circles with highest intensity.
        /// </summary>
        /// 
        /// <param name="count">Amount of circles to get.</param>
        /// 
        /// <returns>Returns arrary of most intesive circles. If there are no circles detected,
        /// <b>null</b> is returned.</returns>
        /// 
        public HoughCircle[] GetMostIntensiveCircles( int count )
        {
            // lines count
            int n = Math.Min( count, circles.Count );

            if ( n == 0 )
                return null;

            // result array
            HoughCircle[] dst = new HoughCircle[n];
            circles.CopyTo( 0, dst, 0, n );

            return dst;
        }

        /// <summary>
        /// Get circles with relative intensity higher then specified value.
        /// </summary>
        /// 
        /// <param name="minRelativeIntensity">Minimum relative intesity of circles.</param>
        /// 
        /// <returns>Returns array of circles. If there are no circles detected,
        /// <b>null</b> is returned.</returns>
        /// 
        public HoughCircle[] GetCirclesByRelativeIntensity( double minRelativeIntensity )
        {
            int count = 0, n = circles.Count;

            while ( ( count < n ) && ( ( (HoughCircle) circles[count] ).RelativeIntensity >= minRelativeIntensity ) )
                count++;

            return GetMostIntensiveCircles( count );
        }


        // Collect circles with intesities greater or equal then specified
        private void CollectCircles( )
        {
            short intensity;
            bool foundGreater;

            // clean circles collection
            circles.Clear( );

            // for each Y coordinate
            for ( int y = 0; y < height; y++ )
            {
                // for each X coordinate
                for ( int x = 0; x < width; x++ )
                {
                    // get current value
                    intensity = houghMap[y, x];

                    if ( intensity < minCircleIntensity )
                        continue;

                    foundGreater = false;

                    // check neighboors
                    for ( int ty = y - localPeakRadius, tyMax = y + localPeakRadius; ty < tyMax; ty++ )
                    {
                        // continue if the coordinate is out of map
                        if ( ty < 0 )
                            continue;
                        // break if it is not local maximum or coordinate is out of map
                        if ( ( foundGreater == true ) || ( ty >= height ) )
                            break;

                        for ( int tx = x - localPeakRadius, txMax = x + localPeakRadius; tx < txMax; tx++ )
                        {
                            // continue or break if the coordinate is out of map
                            if ( tx < 0 )
                                continue;
                            if ( tx >= width )
                                break;

                            // compare the neighboor with current value
                            if ( houghMap[ty, tx] > intensity )
                            {
                                foundGreater = true;
                                break;
                            }
                        }
                    }

                    // was it local maximum ?
                    if ( !foundGreater )
                    {
                        // we have local maximum
                        circles.Add( new HoughCircle( x, y, radiusToDetect, intensity, (double) intensity / maxMapIntensity ) );
                    }
                }
            }

            circles.Sort( );
        }

        // Draw Hough circle:
        // http://www.cs.unc.edu/~mcmillan/comp136/Lecture7/circle.html
        //
        // TODO: more optimizations of circle drawing could be done.
        //
        private void DrawHoughCircle( int xCenter, int yCenter )
        {
            int x = 0;
            int y = radiusToDetect;
            int p = ( 5 - radiusToDetect * 4 ) / 4;

            SetHoughirclePoints( xCenter, yCenter, x, y );

            while ( x < y )
            {
                x++;
                if ( p < 0 )
                {
                    p += 2 * x + 1;
                }
                else
                {
                    y--;
                    p += 2 * ( x - y ) + 1;
                }
                SetHoughirclePoints( xCenter, yCenter, x, y );
            }
        }

        // Set circle points
        private void SetHoughirclePoints( int cx, int cy, int x, int y )
        {
            if ( x == 0 )
            {
                SetHoughPoint( cx, cy + y );
                SetHoughPoint( cx, cy - y );
                SetHoughPoint( cx + y, cy );
                SetHoughPoint( cx - y, cy );
            }
            else if ( x == y )
            {
                SetHoughPoint( cx + x, cy + y );
                SetHoughPoint( cx - x, cy + y );
                SetHoughPoint( cx + x, cy - y );
                SetHoughPoint( cx - x, cy - y );
            }
            else if ( x < y )
            {
                SetHoughPoint( cx + x, cy + y );
                SetHoughPoint( cx - x, cy + y );
                SetHoughPoint( cx + x, cy - y );
                SetHoughPoint( cx - x, cy - y );
                SetHoughPoint( cx + y, cy + x );
                SetHoughPoint( cx - y, cy + x );
                SetHoughPoint( cx + y, cy - x );
                SetHoughPoint( cx - y, cy - x );
            }
        }

        // Set point
        private void SetHoughPoint( int x, int y )
        {
            if ( ( x >= 0 ) && ( y >= 0 ) && ( x < width ) && ( y < height ) )
            {
                houghMap[y, x]++;
            }
        }
    }
}

----------


## asefy2008

چند سوال داشتم :
1.منظور از با distance مشخص نمونه برداری کنیم یعنی چی؟(فرض کنید عکس لبه یابی شده را داریم حالا باید چطوری این کار را اجام بدیم)
2. منظور از سه نقطه کنار هم، هم در راستای محور xها و  هم در راستای محور yها هست دیگه و همچنین مورب رو هم شامل میشه؟
3. fake maximum و fade یعنی چی ؟
4. 


> حالا براي اينكه fake maximum رو fade كنيد بايستي روي كل accumulator گوسين رو اعمال كنيد حالا همه نقاط صحت دارند.


اگه میشه در مورد این جمله بیشتر توضیح بدید؟

----------


## مصطفی ساتکی

جواب اول : sampling یا نمونه برداری یعنی اینکه از کل نمونه ها ، نمونه خاصی را انتخاب کنیم در اینجا لبه های متوالی که داریم رو با فواصل مشخص نمونه برداری می کنیم.
جواب دوم : نقاط مجاور یعنی که نقاطی که در یک مسیر بدون فاصله پشت سرهم قرار گرفتن در ضمن نقاطی که در راستای x یا y قرار دارند یا در زاویای K*pi/4 قرار دارند جز همسایگی محسوب نمیشن.
جواب سوم : یعنی اینکه maximum هایی که ساختگی هستند و صحت ندارند حذف می شن
جواب چهارم : از این روش جهت حذف نویز استفاده میشه

----------


## asefy2008

ممنون از جوابتون



> جواب اول : sampling یا نمونه برداری یعنی اینکه از کل نمونه ها ، نمونه  خاصی را انتخاب کنیم در اینجا لبه های متوالی که داریم رو با فواصل مشخص  نمونه برداری می کنیم.


منظور از لبه های متواالی که داریم با فواصل مشخص نمونه برداری می کنیم این هست که مثلا در لبه ها 5 یا 10 سلول متوالی را به عنوان یک نمونه انتخاب می کنیم؟

----------


## مصطفی ساتکی

بله sampling در واقع به همین معناست

----------


## asefy2008

ممون
در قطعه کدی که قرار دادید من از قطعه کد زیر برای اجرا اون و تشخیص دایره استفاده کردم :

HoughCircleTransformation circleTransform = new HoughCircleTransformation( 35 ); // apply Hough circle transform circleTransform.ProcessImage( sourceImage ); Bitmap houghCirlceImage = circleTransform.ToBitmap( ); // get circles using relative intensity HoughCircle[] circles = circleTransform.GetCirclesByRelativeIntensity( 0.5 );  foreach ( HoughCircle circle in circles ) {     // ... }


ولی در processimage از فرمت عکس ایراد می گیره علتش چیه و چطور میشه رفعش کرد.

نکته : من یک تغییراتی تو اون قسمت دادم و درست شد ولی به هیچ عنوان نمی تونه دایره رو درست تشخیص بده من شکل داخل سایت aforge رو دادم بهش ولی اون جواب رو بهم نداد در صورتی که برای مثالی که خود سایت aforge داده درست کار می کنه(من مثال خود سایت رو دانلود کردم و پایین قرار دادم).

اینم کتغییراتی که من در کد دادم :
public void ProcessImage( Bitmap image )
        {
            // check image format
            if ( image.PixelFormat != PixelFormat.Format32bppArgb )
                throw new ArgumentException( "Pixel format of source image should be 8 bpp indexed" );

            // lock source image
            BitmapData imageData = image.LockBits(
                new Rectangle( 0, 0, image.Width, image.Height ),
                ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb );

            // process the image
            ProcessImage( imageData );

            // unlock image
            image.UnlockBits( imageData );
        }

----------


## مصطفی ساتکی

من اگر توجه کرده باشید در پست شماره 2 به شما عرض کردم که hough circle ط یک localizer خوبی نیست در همون پست برای شما نحوه پیاده یازی circle finder رو توضیح دادم من خودم به شخصه در کارهام از چندین نوع circle finder استفاده می کنم که توسط خودم پیاده شده و طرحش هم از خودم بوده با توجه به شرایط شما می تونید rule هایی رو به هر کدام از circle finder ها اعمال کنید که در همون زمینه مشخص جوابهای robust ی رو به شما میده

----------


## asefy2008

با تشکر از شما 
می خواستم ببینم نمونه کد تبدیل هاف دایروی رو با متلب دارید(یا هر کدی با متلب که بتوننه دایره رو تشخیص بده)؟

در ضمن ممنون از توضیحات قبلیتون که واقعا کمکم کرد وتونستم یک راه جدید برای کارم پیداکنم.

----------


## مصطفی ساتکی

خواهش می کنم.
من با C++‎ کار می کنم ولی به c  اگر بخاید می تونم راهنمایی تون کنم

----------


## asefy2008

ممنون میشم اگه کمک بفرمایید.
فکر کنم بالاخره باید برم سمت C++‎‎ اکثر کدها چه در اینترنت  و چه دوستان می فرماین به متلب و سی هست.

من سرانجام تونستم پروژه تشخیص هویت از طریق عنبیه رو تکمیل کنم ولی بزرگترین مشکلش کندی در پیدا کردن دایره که واقعا برنامه رو کند کرد.(البته دایره داخلی رو با کمک های شما خیلی سریع پیدا می کنه.)

----------


## alibagherian

سلام به دوستان
كسي برنامه تشخيص دايره با متلب را داره ؟

----------

