Emgu CV – SURF Algoritması İle Nesne Takibi

Merhaba,

Şuana kadar görüntü işleme üzerine yapmış olduğumuz tüm çalışmalar neticesinde bir çok Image Processing algoritmasının mevcut olduğundan bahsetmiş bulunmaktayız. Bu algoritmalar arasında; görüntü üzerinde bir nesneyi algılamamızı sağlayan ve bu işlemi yaparken boyut, renk vs. gibi özelliklerinden bağımsız bir şekilde çalışan SURF algoritmasını ele alacağız.

SURF algoritması, yukarıda da ifade etmeye çalıştığım gibi ölçekleme ve rotasyondan bağımsızdır. Bu yüzden değişik bakış açılarına ve ışınsal açılara karşı gayet dirençli olmasından dolayı nesneyi algılamada doğruluk oranı oldukça yükse ve performanslıdır.

Bizler bu makalemizde SURF algoritmasını kullanacağımız sınıf yapımızı inşa edeceğiz ve çalışmamızı bir form üzerinde örneklendireceğiz. Eğer ki, sizlerde bu makaleyle birlikte paralel icraatte bulunmayı düşünüyorsanız hali hazırda bir Windows Forms projesi açınız ve form üzerinde bir adet PictureBox nesnesi ile projeye gerekli EmguCV entegrasyonunu sağlayarak sürece eşlik etmek için hazır bekleyiniz…

Evet…

Şimdi başlayabiliriz…

Öncelikle SURF algoritmamızı barındıran sınıfımızı inşa edelim.

    static class SURFAlgoritmasi
    {
        public static Image<Bgr, Byte> Draw(Image<Gray, Byte> modelImage, Image<Gray, byte> observedImage)
        {
            HomographyMatrix homography = null;
            SURFDetector surfCPU = new SURFDetector(500, false);
            VectorOfKeyPoint modelKeyPoints;
            VectorOfKeyPoint observedKeyPoints;
            Matrix<int> indices;
            Matrix<byte> mask;
            int k = 2;
            double uniquenessThreshold = 0.8;
            if (GpuInvoke.HasCuda)
            {
                GpuSURFDetector surfGPU = new GpuSURFDetector(surfCPU.SURFParams, 0.01f);
                GpuImage<Gray, Byte> gpuModelImage = new GpuImage<Gray, byte>(modelImage);
                //extract features from the object image
                GpuMat<float> gpuModelKeyPoints = surfGPU.DetectKeyPointsRaw(gpuModelImage, null);
                GpuMat<float> gpuModelDescriptors = surfGPU.ComputeDescriptorsRaw(gpuModelImage, null, gpuModelKeyPoints);
                GpuBruteForceMatcher<float> matcher = new GpuBruteForceMatcher<float>(DistanceType.L2);
                modelKeyPoints = new VectorOfKeyPoint();
                surfGPU.DownloadKeypoints(gpuModelKeyPoints, modelKeyPoints);
                GpuImage<Gray, Byte> gpuObservedImage = new GpuImage<Gray, byte>(observedImage);
                GpuMat<float> gpuObservedKeyPoints = surfGPU.DetectKeyPointsRaw(gpuObservedImage, null);
                GpuMat<float> gpuObservedDescriptors = surfGPU.ComputeDescriptorsRaw(gpuObservedImage, null, gpuObservedKeyPoints);
                GpuMat<int> gpuMatchIndices = new GpuMat<int>(gpuObservedDescriptors.Size.Height, k, 1, true);
                GpuMat<float> gpuMatchDist = new GpuMat<float>(gpuObservedDescriptors.Size.Height, k, 1, true);
                GpuMat<Byte> gpuMask = new GpuMat<byte>(gpuMatchIndices.Size.Height, 1, 1);
                Stream stream = new Stream();
                matcher.KnnMatchSingle(gpuObservedDescriptors, gpuModelDescriptors, gpuMatchIndices, gpuMatchDist, k, null, stream);
                indices = new Matrix<int>(gpuMatchIndices.Size);
                mask = new Matrix<byte>(gpuMask.Size);
                GpuMat<float> col0 = gpuMatchDist.Col(0);
                GpuMat<float> col1 = gpuMatchDist.Col(1);
                GpuInvoke.Multiply(col1, new MCvScalar(uniquenessThreshold), col1, stream);
                GpuInvoke.Compare(col0, col1, gpuMask, CMP_TYPE.CV_CMP_LE, stream);
                observedKeyPoints = new VectorOfKeyPoint();
                surfGPU.DownloadKeypoints(gpuObservedKeyPoints, observedKeyPoints);
                stream.WaitForCompletion();
                gpuMask.Download(mask);
                gpuMatchIndices.Download(indices);
                if (GpuInvoke.CountNonZero(gpuMask) >= 4)
                {
                    int nonZeroCount = Features2DToolbox.VoteForSizeAndOrientation(modelKeyPoints, observedKeyPoints, indices, mask, 1.5, 20);
                    if (nonZeroCount >= 4)
                        homography = Features2DToolbox.GetHomographyMatrixFromMatchedFeatures(modelKeyPoints, observedKeyPoints, indices, mask, 2);
                }
            }
            else
            {
                modelKeyPoints = surfCPU.DetectKeyPointsRaw(modelImage, null);
                Matrix<float> modelDescriptors = surfCPU.ComputeDescriptorsRaw(modelImage, null, modelKeyPoints);
                observedKeyPoints = surfCPU.DetectKeyPointsRaw(observedImage, null);
                Matrix<float> observedDescriptors = surfCPU.ComputeDescriptorsRaw(observedImage, null, observedKeyPoints);
                BruteForceMatcher<float> matcher = new BruteForceMatcher<float>(DistanceType.L2);
                matcher.Add(modelDescriptors);
                indices = new Matrix<int>(observedDescriptors.Rows, k);
                Matrix<float> dist = new Matrix<float>(observedDescriptors.Rows, k);
                matcher.KnnMatch(observedDescriptors, indices, dist, k, null);
                mask = new Matrix<byte>(dist.Rows, 1);
                mask.SetValue(255);
                Features2DToolbox.VoteForUniqueness(dist, uniquenessThreshold, mask);
                int nonZeroCount = CvInvoke.cvCountNonZero(mask);
                if (nonZeroCount >= 4)
                {
                    nonZeroCount = Features2DToolbox.VoteForSizeAndOrientation(modelKeyPoints, observedKeyPoints, indices, mask, 1.5, 20);
                    if (nonZeroCount >= 4)
                        homography = Features2DToolbox.GetHomographyMatrixFromMatchedFeatures(modelKeyPoints, observedKeyPoints, indices, mask, 2);
                }
            }
            #region GÖRÜNTÜNÜN İŞLENMESİ
            Image<Bgr, Byte> result = Features2DToolbox.DrawMatches(modelImage, modelKeyPoints, observedImage, observedKeyPoints, indices, new Bgr(255, 255, 255), new Bgr(255, 255, 255), mask, Features2DToolbox.KeypointDrawType.DEFAULT);
            #endregion
            if (homography != null)
            {
                Rectangle rect = modelImage.ROI;
                PointF[] pts = new PointF[] {
               new PointF(rect.Left, rect.Bottom),
               new PointF(rect.Right, rect.Bottom),
               new PointF(rect.Right, rect.Top),
               new PointF(rect.Left, rect.Top)};
                homography.ProjectPoints(pts);

                #region TESPİT EDİLEN NESNENİN ÇİZİLMESİ
                result.DrawPolyline(Array.ConvertAll<PointF, Point>(pts, Point.Round), true, new Bgr(Color.Red), 3);
                #endregion
            }
            return result;
        }
    }

Evet… SURF algoritmasını yukarıdaki gibi oluşturmuş olduk. Yapısal olarak teknik boyutta çok fazla teferruat barındırdığı için şimdilik yukarıdaki kod bloğunun satırlarına izahet getirmiyorum. Lakin bu sınıfın sağlıklı bir şekilde çalıştığından birazdan gözlerimizle emin olacağız.

Şimdi sıra, kameradan elde ettiğimiz görüntüde SURF algoritmasıyla tarayıp algılayacağımız nesneyi sisteme tanıtmaya gelmiştir. Kullanacağımız nesne çok sevip değer verdiğim Sayın Yazar Ümit Doğan Beyefendi’nin “Atatürk’ün İstihbarat Faaliyetleri – Milli Ajanlarımız” isimli kitabıdır.(Kesinlikle okumanızı ve okutturmanızı tavsiye ederim 😉) Velhasıl bu kitabın bir görselini internetten indirip elde edebilir ya da direkt olarak nesneyi kamera üzerinden de manuel olarak tarayıp görselini sağlayabiliriz. Projede bu görseli tutmak için Resource dosyalarını kullandığımı baştan belirteyim.

Algılatacağımız nesnenin görselini temin ederek sisteme entegre ettikten sonra sıra yukarıdaki oluşturmuş olduğumuz static yapıdaki “SURFAlgoritmasi” isimli sınıfı kullanmaya yani içeriğimizin asıl işlevini icra etmeye gelmiştir.

        private void Form1_Load(object sender, EventArgs e)
        {
            Capture capture = new Capture();
            capture.Start();
            capture.ImageGrabbed += (a, b) =>
            {
                try
                {
                    var image = capture.RetrieveBgrFrame();

                    var SURFimage = SURFAlgoritmasi.Draw(new Image<Gray, Byte>(Resimler.nesne), image.Convert<Gray, byte>());
                    pictureBox1.Image = SURFimage.ToBitmap();
                }
                catch
                {

                }
            };
        }

Yukarıdaki kod bloğunda olduğu gibi SURF algoritmasını kullanarak kameradan gelen görüntü üzerinde elimizdeki görüntüleri kıyaslamakta ve nesne tespitini gerçekleştirmekteyiz. Şuana kadar yapmış olduğumuz işlemin faaliyetini aşağıdaki videodan da inceleyebilirsiniz.

Eğer ki, nesnenin görüntü çıktısına dahil edilmeden sadece tespit edilmesini istiyorsanız “SURFAlgoritmasi” sınıfındaki “GÖRÜNTÜNÜN İŞLENMESİ” isimli regionın içerisini aşağıdaki çalışma ile değiştirmeniz yeterlidir.

            Image<Bgr, Byte> result = observedImage.Convert<Bgr, byte>();

Bu şekilde projeyi derleyip çalıştırdığımız vakit SURF algoritmasını aşağıdaki videoda olduğu gibi kullanabilmekteyiz.

Gördüğünüz üzere SURF algoritması ile elimizdeki görüntü üzerinde belirlediğimiz bir nesneyi tarayabilmekte ve algılayabilmekteyiz. Her ne kadar benim ihtiyar bilgisayarım bu algoritmanın hızına hakkıyla eşlik edemesede yine de algoritmanın yüksek performanslı işleyişine ve işlevselliğine şahitlik etmiş bulunmaktasınız.

Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Bunlar da hoşunuza gidebilir...

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

CAPTCHA (Şahıs Denetim Kodu) Resmi

*

Copy Protected by Chetan's WP-Copyprotect.