// --------------------------------- OpenCV license.txt --------------------------- (* // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, installing or using the software you agree to this license. // If you do not agree to this license, do not download, install, // copy or use the software. // // // License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. // Copyright (C) 2009, Willow Garage Inc., all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistribution's of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistribution's in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * The name of the copyright holders may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and // any express or implied warranties, including, but not limited to, the implied // warranties of merchantability and fitness for a particular purpose are disclaimed. // In no event shall the Intel Corporation or contributors be liable for any direct, // indirect, incidental, special, exemplary, or consequential damages // (including, but not limited to, procurement of substitute goods or services; // loss of use, data, or profits; or business interruption) however caused // and on any theory of liability, whether in contract, strict liability, // or tort (including negligence or otherwise) arising in any way out of // the use of this software, even if advised of the possibility of such damage. *) (* / ************************************************************************************************** // Project Delphi-OpenCV // ************************************************************************************************** // Contributor: // laentir Valetov // email:laex@bk.ru // ************************************************************************************************** // You may retrieve the latest version of this file at the GitHub, // located at git://github.com/Laex/Delphi-OpenCV.git // ************************************************************************************************** // License: // The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); // you may not use this file except in compliance with the License. You may obtain a copy of the // License at http://www.mozilla.org/MPL/ // // Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF // ANY KIND, either express or implied. See the License for the specific language governing rights // and limitations under the License. // // Alternatively, the contents of this file may be used under the terms of the // GNU Lesser General Public License (the "LGPL License"), in which case the // provisions of the LGPL License are applicable instead of those above. // If you wish to allow use of your version of this file only under the terms // of the LGPL License and not to allow others to use your version of this file // under the MPL, indicate your decision by deleting the provisions above and // replace them with the notice and other provisions required by the LGPL // License. If you do not delete the provisions above, a recipient may use // your version of this file under either the MPL or the LGPL License. // // For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html // ************************************************************************************************** // Warning: Using Delphi XE3 syntax! // ************************************************************************************************** // The Initial Developer of the Original Code: // OpenCV: open source computer vision library // Homepage: http://opencv.org // Online docs: http://docs.opencv.org // Q&A forum: http://answers.opencv.org // Dev zone: http://code.opencv.org // ************************************************************************************************** // Original file: // opencv\modules\legacy\src\enteringblobdetection.cpp // ************************************************************************************************* *) {$IFDEF DEBUG} {$A8,B-,C+,D+,E-,F-,G+,H+,I+,J-,K-,L+,M-,N+,O-,P+,Q+,R+,S-,T-,U-,V+,W+,X+,Y+,Z1} {$ELSE} {$A8,B-,C-,D-,E-,F-,G+,H+,I+,J-,K-,L-,M-,N+,O+,P+,Q-,R-,S-,T-,U-,V+,W-,X+,Y-,Z1} {$ENDIF} {$WARN SYMBOL_DEPRECATED OFF} {$WARN SYMBOL_PLATFORM OFF} {$WARN UNIT_PLATFORM OFF} {$WARN UNSAFE_TYPE OFF} {$WARN UNSAFE_CODE OFF} {$WARN UNSAFE_CAST OFF} {$POINTERMATH ON} {$TYPEDADDRESS ON} unit enteringblobdetection; interface Uses Windows, core_c, core.types_c, imgproc_c, blobtrack; // static int CompareContour(const void* a, const void* b, void* ) // { // float dx,dy; // float h,w,ht,wt; // CvPoint2D32f pa,pb; // CvRect ra,rb; // CvSeq* pCA = *(CvSeq**)a; // CvSeq* pCB = *(CvSeq**)b; // ra = ((CvContour*)pCA)->rect; // rb = ((CvContour*)pCB)->rect; // pa.x = ra.x + ra.width*0.5f; // pa.y = ra.y + ra.height*0.5f; // pb.x = rb.x + rb.width*0.5f; // pb.y = rb.y + rb.height*0.5f; // w = (ra.width+rb.width)*0.5f; // h = (ra.height+rb.height)*0.5f; // // dx = (float)(fabs(pa.x - pb.x)-w); // dy = (float)(fabs(pa.y - pb.y)-h); // // //wt = MAX(ra.width,rb.width)*0.1f; // wt = 0; // ht = MAX(ra.height,rb.height)*0.3f; // return (dx < wt && dy < ht); // } // procedure cvFindBlobsByCCClasters(pFG: pIplImage; pBlobs: TCvBlobSeq; storage: pCvMemStorage); const /// * Simple blob detector. */ /// * Number of successive frame to analyse: */ EBD_FRAME_NUM = 5; type TCvBlobDetectorSimple = class(TCvBlobDetector) public constructor Create; destructor Destroy; override; function DetectNewBlob(pImg: pIplImage; pFGMask: pIplImage; pNewBlobList: TCvBlobSeq; pOldBlobList: TCvBlobSeq) : Integer; override; procedure Release(); override; { delete this; } protected m_pMaskBlobNew: pIplImage; m_pMaskBlobExist: pIplImage; // * Lists of connected components detected on previous frames: */ m_pBlobLists: array [0 .. EBD_FRAME_NUM - 1] of TCvBlobSeq; end; /// * Constructor of BlobDetector: */ // CvBlobDetectorSimple::CvBlobDetectorSimple() // { // int i = 0; // m_pMaskBlobNew = NULL; // m_pMaskBlobExist = NULL; // for(i=0;iwidth,pFGMask->height); // if(m_pMaskBlobNew == NULL ) m_pMaskBlobNew = cvCreateImage(S,IPL_DEPTH_8U,1); // if(m_pMaskBlobExist == NULL ) m_pMaskBlobExist = cvCreateImage(S,IPL_DEPTH_8U,1); // // /* Shift blob list: */ // { // int i; // if(m_pBlobLists[0]) delete m_pBlobLists[0]; // for(i=1;ih_next) // { // CvBlob NewBlob; // // /* Image moments: */ // double M00,X,Y,XX,YY; // CvMoments m; // CvRect r = ((CvContour*)cnt)->rect; // CvMat mat; // // if(r.height < S.height*0.02 || r.width < S.width*0.02) continue; // // cvMoments( cvGetSubRect(m_pMaskBlobNew,&mat,r), &m, 0 ); // M00 = cvGetSpatialMoment( &m, 0, 0 ); // // if(M00 <= 0 ) continue; // // X = cvGetSpatialMoment( &m, 1, 0 )/M00; // Y = cvGetSpatialMoment( &m, 0, 1 )/M00; // // XX = (cvGetSpatialMoment( &m, 2, 0 )/M00) - X*X; // YY = (cvGetSpatialMoment( &m, 0, 2 )/M00) - Y*Y; // // NewBlob = cvBlob(r.x+(float)X,r.y+(float)Y,(float)(4*sqrt(XX)),(float)(4*sqrt(YY))); // // Blobs.AddBlob(&NewBlob); // // } /* Next contour. */ // // cvReleaseImage(&pIB); // // } /* One contour - one blob. */ // #endif // // { /* Delete small and intersected blobs: */ // int i; // for(i=Blobs.GetBlobNum(); i>0; i--) // { // CvBlob* pB = Blobs.GetBlob(i-1); // // if(pB->h < S.height*0.02 || pB->w < S.width*0.02) // { // Blobs.DelBlob(i-1); // continue; // } // if(pOldBlobList) // { // int j; // for(j=pOldBlobList->GetBlobNum(); j>0; j--) // { // CvBlob* pBOld = pOldBlobList->GetBlob(j-1); // if((fabs(pBOld->x-pB->x) < (CV_BLOB_RX(pBOld)+CV_BLOB_RX(pB))) && // (fabs(pBOld->y-pB->y) < (CV_BLOB_RY(pBOld)+CV_BLOB_RY(pB)))) // { /* Intersection is present, so delete blob from list: */ // Blobs.DelBlob(i-1); // break; // } // } /* Check next old blob. */ // } /* if pOldBlobList */ // } /* Check next blob. */ // } /* Delete small and intersected blobs. */ // // { /* Bubble-sort blobs by size: */ // int N = Blobs.GetBlobNum(); // int i,j; // for(i=1; i0; --j) // { // CvBlob temp; // float AreaP, AreaN; // CvBlob* pP = Blobs.GetBlob(j-1); // CvBlob* pN = Blobs.GetBlob(j); // AreaP = CV_BLOB_WX(pP)*CV_BLOB_WY(pP); // AreaN = CV_BLOB_WX(pN)*CV_BLOB_WY(pN); // if(AreaN < AreaP)break; // temp = pN[0]; // pN[0] = pP[0]; // pP[0] = temp; // } // } // // /* Copy only first 10 blobs: */ // for(i=0; iAddBlob(Blobs.GetBlob(i)); // } // // } /* Sort blobs by size. */ // // cvReleaseMemStorage(&storage); // // } /* Create blobs. */ // // /* Analyze blob list to find best blob trajectory: */ // { // int Count = 0; // int pBLIndex[EBD_FRAME_NUM]; // int pBL_BEST[EBD_FRAME_NUM]; // int i; // int finish = 0; // double BestError = -1; // int Good = 1; // // for(i=0; iGetBlobNum() == 0) // Good = 0; // // if(Good) // do{ /* For each configuration: */ // CvBlob* pBL[EBD_FRAME_NUM]; // int good = 1; // double Error = 0; // CvBlob* pBNew = m_pBlobLists[EBD_FRAME_NUM-1]->GetBlob(pBLIndex[EBD_FRAME_NUM-1]); // // for(i=0; iGetBlob(pBLIndex[i]); // // Count++; // // /* Check intersection last blob with existed: */ // if(good && pOldBlobList) // { /* Check intersection last blob with existed: */ // int k; // for(k=pOldBlobList->GetBlobNum(); k>0; --k) // { // CvBlob* pBOld = pOldBlobList->GetBlob(k-1); // if((fabs(pBOld->x-pBNew->x) < (CV_BLOB_RX(pBOld)+CV_BLOB_RX(pBNew))) && // (fabs(pBOld->y-pBNew->y) < (CV_BLOB_RY(pBOld)+CV_BLOB_RY(pBNew)))) // good = 0; // } // } /* Check intersection last blob with existed. */ // // /* Check distance to image border: */ // if(good) // { /* Check distance to image border: */ // CvBlob* pB = pBNew; // float dx = MIN(pB->x,S.width-pB->x)/CV_BLOB_RX(pB); // float dy = MIN(pB->y,S.height-pB->y)/CV_BLOB_RY(pB); // // if(dx < 1.1 || dy < 1.1) good = 0; // } /* Check distance to image border. */ // // /* Check uniform motion: */ // if(good) // { // int N = EBD_FRAME_NUM; // float sum[2] = {0,0}; // float jsum[2] = {0,0}; // float a[2],b[2]; /* estimated parameters of moving x(t) = a*t+b*/ // // int j; // for(j=0; jx; // float y = pBL[j]->y; // sum[0] += x; // jsum[0] += j*x; // sum[1] += y; // jsum[1] += j*y; // } // // a[0] = 6*((1-N)*sum[0]+2*jsum[0])/(N*(N*N-1)); // b[0] = -2*((1-2*N)*sum[0]+3*jsum[0])/(N*(N+1)); // a[1] = 6*((1-N)*sum[1]+2*jsum[1])/(N*(N*N-1)); // b[1] = -2*((1-2*N)*sum[1]+3*jsum[1])/(N*(N+1)); // // for(j=0; jx,2)+ // pow(a[1]*j+b[1]-pBL[j]->y,2); // } // // Error = sqrt(Error/N); // // if( Error > S.width*0.01 || // fabs(a[0])>S.width*0.1 || // fabs(a[1])>S.height*0.1) // good = 0; // // } /* Check configuration. */ // // // /* New best trajectory: */ // if(good && (BestError == -1 || BestError > Error)) // { // for(i=0; iGetBlobNum()) break; // pBLIndex[i]=0; // } /* Next time shift. */ // // if(i==EBD_FRAME_NUM)finish=1; // // } while(!finish); /* Check next time configuration of connected components. */ // // #if 0 // {/**/ // printf("BlobDetector configurations = %d [",Count); // int i; // for(i=0; iGetBlobNum():0); // } // printf("]\n"); // // } // #endif // // if(BestError != -1) // { /* Write new blob to output and delete from blob list: */ // CvBlob* pNewBlob = m_pBlobLists[EBD_FRAME_NUM-1]->GetBlob(pBL_BEST[EBD_FRAME_NUM-1]); // pNewBlobList->AddBlob(pNewBlob); // // for(i=0; iDelBlob(pBL_BEST[i]); // } /* Remove blob from each list. */ // // result = 1; // // } /* Write new blob to output and delete from blob list. */ // } /* Analyze blob list to find best blob trajectory. */ // // return result; // // } /* cvDetectNewBlob */ const /// * Simple blob detector2. */ /// * Number of successive frames to analyse: */ SEQ_SIZE_MAX = 30; SEQ_NUM = 1000; Type pDefSeq = ^TDefSeq; TDefSeq = packed record size: Integer; pBlobs: array [0 .. SEQ_SIZE_MAX - 1] of pCvBlob; end; Type TCvBlobDetectorCC = class(TCvBlobDetector) public constructor Create; destructor Destroy; override; function DetectNewBlob(pImg: pIplImage; pFGMask: pIplImage; pNewBlobList: TCvBlobSeq; pOldBlobList: TCvBlobSeq) : Integer; override; procedure Release(); override; { delete this; } procedure ParamUpdate(); override; // { // if(SEQ_SIZE<1)SEQ_SIZE=1; // if(SEQ_SIZE>SEQ_SIZE_MAX)SEQ_SIZE=SEQ_SIZE_MAX; // // #ifdef USE_OBJECT_DETECTOR // if( m_param_split_detector_file_name ) // { // m_split_detector = new CvObjectDetector(); // if( !m_split_detector->Load( m_param_split_detector_file_name ) ) // { // delete m_split_detector; // m_split_detector = 0; // } // else // { // m_min_window_size = m_split_detector->GetMinWindowSize(); // m_max_border = m_split_detector->GetMaxBorderSize(); // } // } // #endif // } private // * Lists of connected components detected on previous frames: */ m_pBlobLists: array [0 .. SEQ_SIZE_MAX - 1] of TCvBlobSeq; m_TrackSeq: array [0 .. SEQ_NUM - 1] of TDefSeq; m_TrackNum: Integer; m_HMin: Single; m_WMin: Single; m_MinDistToBorder: Single; m_Clastering: Integer; SEQ_SIZE: Integer; // /* If not 0 then the detector is loaded from the specified file // * and it is applied for splitting blobs which actually correspond // * to groups of objects: // */ m_param_split_detector_file_name: pCvChar; m_param_roi_scale: Single; m_param_only_roi: Integer; m_split_detector: TCvObjectDetector; m_min_window_size: TCvSize; m_max_border: Integer; m_detected_blob_seq: TCvBlobSeq; m_roi_seq: pCvSeq; m_debug_blob_seq: TCvBlobSeq; end; /// * Constructor for BlobDetector: */ // CvBlobDetectorCC::CvBlobDetectorCC() : // m_split_detector(0), // m_detected_blob_seq(sizeof(CvDetectedBlob)), // m_roi_seq(0), // m_debug_blob_seq(sizeof(CvDetectedBlob)) // { // /*CvDrawShape shapes[] = // { // { CvDrawShape::RECT, {{255,255,255}} }, // { CvDrawShape::RECT, {{0,0,255}} }, // { CvDrawShape::ELLIPSE, {{0,255,0}} } // }; // int num_shapes = sizeof(shapes) / sizeof(shapes[0]);*/ // // int i = 0; // SEQ_SIZE = 10; // AddParam("Latency",&SEQ_SIZE); // for(i=0;istorage ); // m_roi_seq = 0; // } // //cvDestroyWindow( "EnteringBlobDetectionDebug" ); // } /* cvReleaseBlobDetector */ // // /// * cvDetectNewBlobs // * Return 1 and fill blob pNewBlob with // * blob parameters if new blob is detected: // */ // int CvBlobDetectorCC::DetectNewBlob(IplImage* /*pImg*/, IplImage* pFGMask, CvBlobSeq* pNewBlobList, CvBlobSeq* pOldBlobList) // { // int result = 0; // CvSize S = cvSize(pFGMask->width,pFGMask->height); // // /* Shift blob list: */ // { // int i; // if(m_pBlobLists[SEQ_SIZE-1]) delete m_pBlobLists[SEQ_SIZE-1]; // // for(i=SEQ_SIZE-1; i>0; --i) m_pBlobLists[i] = m_pBlobLists[i-1]; // // m_pBlobLists[0] = new CvBlobSeq; // // } /* Shift blob list. */ // // /* Create contours and add new blobs to blob list: */ // { /* Create blobs: */ // CvBlobSeq Blobs; // CvMemStorage* storage = cvCreateMemStorage(); // // if(m_Clastering) // { /* Glue contours: */ // cvFindBlobsByCCClasters(pFGMask, &Blobs, storage ); // } /* Glue contours. */ // else // { /**/ // IplImage* pIB = cvCloneImage(pFGMask); // CvSeq* cnts = NULL; // CvSeq* cnt = NULL; // cvThreshold(pIB,pIB,128,255,CV_THRESH_BINARY); // cvFindContours(pIB,storage, &cnts, sizeof(CvContour), CV_RETR_EXTERNAL); // // /* Process each contour: */ // for(cnt = cnts; cnt; cnt=cnt->h_next) // { // CvBlob NewBlob; // /* Image moments: */ // double M00,X,Y,XX,YY; // CvMoments m; // CvRect r = ((CvContour*)cnt)->rect; // CvMat mat; // if(r.height < S.height*m_HMin || r.width < S.width*m_WMin) continue; // cvMoments( cvGetSubRect(pFGMask,&mat,r), &m, 0 ); // M00 = cvGetSpatialMoment( &m, 0, 0 ); // if(M00 <= 0 ) continue; // X = cvGetSpatialMoment( &m, 1, 0 )/M00; // Y = cvGetSpatialMoment( &m, 0, 1 )/M00; // XX = (cvGetSpatialMoment( &m, 2, 0 )/M00) - X*X; // YY = (cvGetSpatialMoment( &m, 0, 2 )/M00) - Y*Y; // NewBlob = cvBlob(r.x+(float)X,r.y+(float)Y,(float)(4*sqrt(XX)),(float)(4*sqrt(YY))); // Blobs.AddBlob(&NewBlob); // // } /* Next contour. */ // // cvReleaseImage(&pIB); // // } /* One contour - one blob. */ // // { /* Delete small and intersected blobs: */ // int i; // for(i=Blobs.GetBlobNum(); i>0; i--) // { // CvBlob* pB = Blobs.GetBlob(i-1); // // if(pB->h < S.height*m_HMin || pB->w < S.width*m_WMin) // { // Blobs.DelBlob(i-1); // continue; // } // // if(pOldBlobList) // { // int j; // for(j=pOldBlobList->GetBlobNum(); j>0; j--) // { // CvBlob* pBOld = pOldBlobList->GetBlob(j-1); // if((fabs(pBOld->x-pB->x) < (CV_BLOB_RX(pBOld)+CV_BLOB_RX(pB))) && // (fabs(pBOld->y-pB->y) < (CV_BLOB_RY(pBOld)+CV_BLOB_RY(pB)))) // { /* Intersection detected, delete blob from list: */ // Blobs.DelBlob(i-1); // break; // } // } /* Check next old blob. */ // } /* if pOldBlobList. */ // } /* Check next blob. */ // } /* Delete small and intersected blobs. */ // // { /* Bubble-sort blobs by size: */ // int N = Blobs.GetBlobNum(); // int i,j; // for(i=1; i0; --j) // { // CvBlob temp; // float AreaP, AreaN; // CvBlob* pP = Blobs.GetBlob(j-1); // CvBlob* pN = Blobs.GetBlob(j); // AreaP = CV_BLOB_WX(pP)*CV_BLOB_WY(pP); // AreaN = CV_BLOB_WX(pN)*CV_BLOB_WY(pN); // if(AreaN < AreaP)break; // temp = pN[0]; // pN[0] = pP[0]; // pP[0] = temp; // } // } // // /* Copy only first 10 blobs: */ // for(i=0; iAddBlob(Blobs.GetBlob(i)); // } // // } /* Sort blobs by size. */ // // cvReleaseMemStorage(&storage); // // } /* Create blobs. */ // // { /* Shift each track: */ // int j; // for(j=0; j0; --i) // pTrack->pBlobs[i] = pTrack->pBlobs[i-1]; // // pTrack->pBlobs[0] = NULL; // if(pTrack->size == SEQ_SIZE)pTrack->size--; // } // } /* Shift each track. */ // // /* Analyze blob list to find best blob trajectory: */ // { // double BestError = -1; // int BestTrack = -1;; // CvBlobSeq* pNewBlobs = m_pBlobLists[0]; // int i; // int NewTrackNum = 0; // for(i=pNewBlobs->GetBlobNum(); i>0; --i) // { // CvBlob* pBNew = pNewBlobs->GetBlob(i-1); // int j; // int AsignedTrack = 0; // for(j=0; jsize>0?pTrack->pBlobs[1]:NULL; // if(pLastBlob == NULL) continue; // dx = fabs(CV_BLOB_X(pLastBlob)-CV_BLOB_X(pBNew)); // dy = fabs(CV_BLOB_Y(pLastBlob)-CV_BLOB_Y(pBNew)); // if(dx > 2*CV_BLOB_WX(pLastBlob) || dy > 2*CV_BLOB_WY(pLastBlob)) continue; // AsignedTrack++; // // if(pTrack->pBlobs[0]==NULL) // { /* Fill existed track: */ // pTrack->pBlobs[0] = pBNew; // pTrack->size++; // } // else if((m_TrackNum+NewTrackNum)pBlobs[0]; // if(pTrack->size != SEQ_SIZE) continue; // if(pBNew == NULL ) continue; // // /* Check intersection last blob with existed: */ // if(Good && pOldBlobList) // { // int k; // for(k=pOldBlobList->GetBlobNum(); k>0; --k) // { // CvBlob* pBOld = pOldBlobList->GetBlob(k-1); // if((fabs(pBOld->x-pBNew->x) < (CV_BLOB_RX(pBOld)+CV_BLOB_RX(pBNew))) && // (fabs(pBOld->y-pBNew->y) < (CV_BLOB_RY(pBOld)+CV_BLOB_RY(pBNew)))) // Good = 0; // } // } /* Check intersection last blob with existed. */ // // /* Check distance to image border: */ // if(Good) // { /* Check distance to image border: */ // float dx = MIN(pBNew->x,S.width-pBNew->x)/CV_BLOB_RX(pBNew); // float dy = MIN(pBNew->y,S.height-pBNew->y)/CV_BLOB_RY(pBNew); // if(dx < m_MinDistToBorder || dy < m_MinDistToBorder) Good = 0; // } /* Check distance to image border. */ // // /* Check uniform motion: */ // if(Good) // { /* Check uniform motion: */ // double Error = 0; // int N = pTrack->size; // CvBlob** pBL = pTrack->pBlobs; // float sum[2] = {0,0}; // float jsum[2] = {0,0}; // float a[2],b[2]; /* estimated parameters of moving x(t) = a*t+b*/ // int j; // // for(j=0; jx; // float y = pBL[j]->y; // sum[0] += x; // jsum[0] += j*x; // sum[1] += y; // jsum[1] += j*y; // } // // a[0] = 6*((1-N)*sum[0]+2*jsum[0])/(N*(N*N-1)); // b[0] = -2*((1-2*N)*sum[0]+3*jsum[0])/(N*(N+1)); // a[1] = 6*((1-N)*sum[1]+2*jsum[1])/(N*(N*N-1)); // b[1] = -2*((1-2*N)*sum[1]+3*jsum[1])/(N*(N+1)); // // for(j=0; jx,2)+ // pow(a[1]*j+b[1]-pBL[j]->y,2); // } // // Error = sqrt(Error/N); // // if( Error > S.width*0.01 || // fabs(a[0])>S.width*0.1 || // fabs(a[1])>S.height*0.1) // Good = 0; // // /* New best trajectory: */ // if(Good && (BestError == -1 || BestError > Error)) // { /* New best trajectory: */ // BestTrack = i; // BestError = Error; // } /* New best trajectory. */ // } /* Check uniform motion. */ // } /* Next track. */ // // #if 0 // { /**/ // printf("BlobDetector configurations = %d [",m_TrackNum); // int i; // for(i=0; iGetBlobNum():0); // } // printf("]\n"); // } // #endif // // if(BestTrack >= 0) // { /* Put new blob to output and delete from blob list: */ // assert(m_TrackSeq[BestTrack].size == SEQ_SIZE); // assert(m_TrackSeq[BestTrack].pBlobs[0]); // pNewBlobList->AddBlob(m_TrackSeq[BestTrack].pBlobs[0]); // m_TrackSeq[BestTrack].pBlobs[0] = NULL; // m_TrackSeq[BestTrack].size--; // result = 1; // } /* Put new blob to output and mark in blob list to delete. */ // } /* Analyze blod list to find best blob trajectory. */ // // { /* Delete bad tracks: */ // int i; // for(i=m_TrackNum-1; i>=0; --i) // { /* Delete bad tracks: */ // if(m_TrackSeq[i].pBlobs[0]) continue; // if(m_TrackNum>0) // m_TrackSeq[i] = m_TrackSeq[--m_TrackNum]; // } /* Delete bad tracks: */ // } // // #ifdef USE_OBJECT_DETECTOR // if( m_split_detector && pNewBlobList->GetBlobNum() > 0 ) // { // int num_new_blobs = pNewBlobList->GetBlobNum(); // int i = 0; // // if( m_roi_seq ) cvClearSeq( m_roi_seq ); // m_debug_blob_seq.Clear(); // for( i = 0; i < num_new_blobs; ++i ) // { // CvBlob* b = pNewBlobList->GetBlob(i); // CvMat roi_stub; // CvMat* roi_mat = 0; // CvMat* scaled_roi_mat = 0; // // CvDetectedBlob d_b = cvDetectedBlob( CV_BLOB_X(b), CV_BLOB_Y(b), CV_BLOB_WX(b), CV_BLOB_WY(b), 0 ); // m_debug_blob_seq.AddBlob(&d_b); // // float scale = m_param_roi_scale * m_min_window_size.height / CV_BLOB_WY(b); // // float b_width = MAX(CV_BLOB_WX(b), m_min_window_size.width / scale) // + (m_param_roi_scale - 1.0F) * (m_min_window_size.width / scale) // + 2.0F * m_max_border / scale; // float b_height = CV_BLOB_WY(b) * m_param_roi_scale + 2.0F * m_max_border / scale; // // CvRect roi = cvRectIntersection( cvRect( cvFloor(CV_BLOB_X(b) - 0.5F*b_width), // cvFloor(CV_BLOB_Y(b) - 0.5F*b_height), // cvCeil(b_width), cvCeil(b_height) ), // cvRect( 0, 0, pImg->width, pImg->height ) ); // if( roi.width <= 0 || roi.height <= 0 ) // continue; // // if( m_roi_seq ) cvSeqPush( m_roi_seq, &roi ); // // roi_mat = cvGetSubRect( pImg, &roi_stub, roi ); // scaled_roi_mat = cvCreateMat( cvCeil(scale*roi.height), cvCeil(scale*roi.width), CV_8UC3 ); // cvResize( roi_mat, scaled_roi_mat ); // // m_detected_blob_seq.Clear(); // m_split_detector->Detect( scaled_roi_mat, &m_detected_blob_seq ); // cvReleaseMat( &scaled_roi_mat ); // // for( int k = 0; k < m_detected_blob_seq.GetBlobNum(); ++k ) // { // CvDetectedBlob* b = (CvDetectedBlob*) m_detected_blob_seq.GetBlob(k); // // /* scale and shift each detected blob back to the original image coordinates */ // CV_BLOB_X(b) = CV_BLOB_X(b) / scale + roi.x; // CV_BLOB_Y(b) = CV_BLOB_Y(b) / scale + roi.y; // CV_BLOB_WX(b) /= scale; // CV_BLOB_WY(b) /= scale; // // CvDetectedBlob d_b = cvDetectedBlob( CV_BLOB_X(b), CV_BLOB_Y(b), CV_BLOB_WX(b), CV_BLOB_WY(b), 1, // b->response ); // m_debug_blob_seq.AddBlob(&d_b); // } // // if( m_detected_blob_seq.GetBlobNum() > 1 ) // { // /* // * Split blob. // * The original blob is replaced by the first detected blob, // * remaining detected blobs are added to the end of the sequence: // */ // CvBlob* first_b = m_detected_blob_seq.GetBlob(0); // CV_BLOB_X(b) = CV_BLOB_X(first_b); CV_BLOB_Y(b) = CV_BLOB_Y(first_b); // CV_BLOB_WX(b) = CV_BLOB_WX(first_b); CV_BLOB_WY(b) = CV_BLOB_WY(first_b); // // for( int j = 1; j < m_detected_blob_seq.GetBlobNum(); ++j ) // { // CvBlob* detected_b = m_detected_blob_seq.GetBlob(j); // pNewBlobList->AddBlob(detected_b); // } // } // } /* For each new blob. */ // // for( i = 0; i < pNewBlobList->GetBlobNum(); ++i ) // { // CvBlob* b = pNewBlobList->GetBlob(i); // CvDetectedBlob d_b = cvDetectedBlob( CV_BLOB_X(b), CV_BLOB_Y(b), CV_BLOB_WX(b), CV_BLOB_WY(b), 2 ); // m_debug_blob_seq.AddBlob(&d_b); // } // } // if( m_split_detector ) // #endif // // return result; // // } /* cvDetectNewBlob */ implementation Uses imgproc.types_c, Math; { TCvBlobDetectorSimple } constructor TCvBlobDetectorSimple.Create; var i: Integer; begin m_pMaskBlobNew := NiL; m_pMaskBlobExist := NiL; for i := 0 to EBD_FRAME_NUM - 1 do m_pBlobLists[i] := NiL; SetModuleName('Simple'); end; destructor TCvBlobDetectorSimple.Destroy; var i: Integer; begin if Assigned(m_pMaskBlobExist) then cvReleaseImage(m_pMaskBlobExist); if Assigned(m_pMaskBlobNew) then cvReleaseImage(m_pMaskBlobNew); for i := 0 to EBD_FRAME_NUM - 1 do m_pBlobLists[i].Free; inherited; end; function TCvBlobDetectorSimple.DetectNewBlob(pImg, pFGMask: pIplImage; pNewBlobList, pOldBlobList: TCvBlobSeq): Integer; Var S: TCvSize; i: Integer; Blobs: TCvBlobSeq; storage: pCvMemStorage; pIB: pIplImage; cnts: pCvSeq; cnt: pCvSeq; NewBlob: TCvBlob; M00, X, Y, XX, YY: double; m: TCvMoments; r: TCvRect; mat: TCvMat; pB: pCvBlob; j: Integer; pBOld: pCvBlob; N: Integer; temp: TCvBlob; AreaP, AreaN: Single; pP: pCvBlob; pN: pCvBlob; Count: Integer; pBLIndex: array [0 .. EBD_FRAME_NUM - 1] of Integer; pBL_BEST: array [0 .. EBD_FRAME_NUM - 1] of Integer; finish: Integer; BestError: double; Good: Integer; pBL: array [0 .. EBD_FRAME_NUM - 1] of pCvBlob; Error: double; pBNew: pCvBlob; k: Integer; dx, dy: Single; sum, jsum, // estimated parameters of moving x(t) := a*t+b*/ a, b: array [0 .. 1] of Single; pNewBlob: pCvBlob; begin Result := 0; S := cvSize(pFGMask^.width, pFGMask^.height); if (m_pMaskBlobNew = nil) then m_pMaskBlobNew := cvCreateImage(S, IPL_DEPTH_8U, 1); if (m_pMaskBlobExist = nil) then m_pMaskBlobExist := cvCreateImage(S, IPL_DEPTH_8U, 1); // Shift blob list: */ if Assigned(m_pBlobLists[0]) then m_pBlobLists[0].Free; for i := 1 to EBD_FRAME_NUM - 1 do m_pBlobLists[i - 1] := m_pBlobLists[i]; m_pBlobLists[EBD_FRAME_NUM - 1] := TCvBlobSeq.Create; // Shift blob list. */ // Create exist blob mask: */ cvCopy(pFGMask, m_pMaskBlobNew); // Create contours and add new blobs to blob list: */ begin // Create blobs: */ storage := cvCreateMemStorage(); {$IFDEF 1} // Glue contours: */ cvFindBlobsByCCClasters(m_pMaskBlobNew, &Blobs, storage); // Glue contours. */ {$ELSE} // */ pIB := cvCloneImage(m_pMaskBlobNew); cnts := nil; cnt := nil; cvThreshold(pIB, pIB, 128, 255, CV_THRESH_BINARY); cvFindContours(pIB, storage, cnts, sizeof(TCvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0)); // Process each contour: */ cnt := cnts; while Assigned(cnt) do // cnt = cnt.h_next) begin // Image moments: */ r := pCvContour(cnt).rect; if (r.height < S.height * 0.02) or (r.width < S.width * 0.02) then continue; CvMoments(cvGetSubRect(m_pMaskBlobNew, @mat, r), @m, 0); M00 := cvGetSpatialMoment(@m, 0, 0); if (M00 <= 0) then continue; X := cvGetSpatialMoment(@m, 1, 0) / M00; Y := cvGetSpatialMoment(@m, 0, 1) / M00; XX := (cvGetSpatialMoment(@m, 2, 0) / M00) - X * X; YY := (cvGetSpatialMoment(@m, 0, 2) / M00) - Y * Y; NewBlob := CvBlob(r.X + X, r.Y + Y, (4 * sqrt(XX)), (4 * sqrt(YY))); Blobs.AddBlob(@NewBlob); cnt := cnt.h_next end; // Next contour. */ cvReleaseImage(&pIB); // One contour - one blob. */ {$ENDIF} begin // Delete small and intersected blobs: */ for i := Blobs.GetBlobNum() downto 1 do begin pB := Blobs.GetBlob(i - 1); if (pB.h < S.height * 0.02) or (pB.w < S.width * 0.02) then begin Blobs.DelBlob(i - 1); continue; end; if Assigned(pOldBlobList) then begin for j := pOldBlobList.GetBlobNum() downto 1 do begin pBOld := pOldBlobList.GetBlob(j - 1); if ((abs(pBOld.X - pB.X) < (CV_BLOB_RX(pBOld) + CV_BLOB_RX(pB)))) and ((abs(pBOld.Y - pB.Y) < (CV_BLOB_RY(pBOld) + CV_BLOB_RY(pB)))) then begin // Intersection is present, so delete blob from list: */ Blobs.DelBlob(i - 1); break; end; end; // Check next old blob. */ end; // if pOldBlobList */ end; // Check next blob. */ end; // Delete small and intersected blobs. */ begin // Bubble-sort blobs by size: */ N := Blobs.GetBlobNum(); for i := 1 to N - 1 do begin for j := i downto 1 do begin pP := Blobs.GetBlob(j - 1); pN := Blobs.GetBlob(j); AreaP := CV_BLOB_WX(pP) * CV_BLOB_WY(pP); AreaN := CV_BLOB_WX(pN) * CV_BLOB_WY(pN); if (AreaN < AreaP) then break; temp := pN[0]; pN[0] := pP[0]; pP[0] := temp; end; end; // Copy only first 10 blobs: */ for i := 0 to MIN(N, 10) - 1 do begin m_pBlobLists[EBD_FRAME_NUM - 1].AddBlob(Blobs.GetBlob(i)); end; end; // Sort blobs by size. */ cvReleaseMemStorage(storage); end; // Create blobs. */ // Analyze blob list to find best blob trajectory: */ begin Count := 0; finish := 0; BestError := -1; Good := 1; for i := 0 to EBD_FRAME_NUM - 1 do begin pBLIndex[i] := 0; pBL_BEST[i] := 0; end; // Check configuration exist: */ i := 0; while (Good <> 0) and (i < EBD_FRAME_NUM) do begin if (m_pBlobLists[i] = nil) or (m_pBlobLists[i].GetBlobNum() = 0) then Good := 0; Inc(i); end; if (Good <> 0) then repeat begin // For each configuration: */ Good := 1; Error := 0; pBNew := m_pBlobLists[EBD_FRAME_NUM - 1].GetBlob(pBLIndex[EBD_FRAME_NUM - 1]); for i := 0 to EBD_FRAME_NUM - 1 do pBL[i] := m_pBlobLists[i].GetBlob(pBLIndex[i]); Inc(Count); // Check intersection last blob with existed: */ if (Good <> 0) and Assigned(pOldBlobList) then begin // Check intersection last blob with existed: */ for k := pOldBlobList.GetBlobNum() downto 1 do begin pBOld := pOldBlobList.GetBlob(k - 1); if ((abs(pBOld.X - pBNew.X) < (CV_BLOB_RX(pBOld) + CV_BLOB_RX(pBNew)))) and ((abs(pBOld.Y - pBNew.Y) < (CV_BLOB_RY(pBOld) + CV_BLOB_RY(pBNew)))) then Good := 0; end; end; // Check intersection last blob with existed. */ // Check distance to image border: */ if (Good <> 0) then begin // Check distance to image border: */ pB := pBNew; dx := MIN(pB.X, S.width - pB.X) / CV_BLOB_RX(pB); dy := MIN(pB.Y, S.height - pB.Y) / CV_BLOB_RY(pB); if (dx < 1.1) or (dy < 1.1) then Good := 0; end; // Check distance to image border. */ // Check uniform motion: */ if (Good <> 0) then begin N := EBD_FRAME_NUM; sum[0] := 0; sum[1] := 0; jsum := sum; for j := 0 to N - 1 do begin X := pBL[j].X; Y := pBL[j].Y; sum[0] := sum[0] + X; jsum[0] := jsum[0] + j * X; sum[1] := sum[1] + Y; jsum[1] := jsum[1] + j * Y; end; a[0] := 6 * ((1 - N) * sum[0] + 2 * jsum[0]) / (N * (N * N - 1)); b[0] := -2 * ((1 - 2 * N) * sum[0] + 3 * jsum[0]) / (N * (N + 1)); a[1] := 6 * ((1 - N) * sum[1] + 2 * jsum[1]) / (N * (N * N - 1)); b[1] := -2 * ((1 - 2 * N) * sum[1] + 3 * jsum[1]) / (N * (N + 1)); for j := 0 to N - 1 do begin Error := Error + power(a[0] * j + b[0] - pBL[j].X, 2) + power(a[1] * j + b[1] - pBL[j].Y, 2); end; Error := sqrt(Error / N); if (Error > S.width * 0.01) or (abs(a[0]) > S.width * 0.1) or (abs(a[1]) > S.height * 0.1) then Good := 0; end; // Check configuration. */ // New best trajectory: */ if (Good <> 0) and ((BestError = -1) or (BestError > Error)) then begin for i := 0 to EBD_FRAME_NUM - 1 do begin pBL_BEST[i] := pBLIndex[i]; end; BestError := Error; end; // New best trajectory. */ // Set next configuration: */ for i := 0 to EBD_FRAME_NUM - 1 do begin Inc(pBLIndex[i]); if (pBLIndex[i] <> m_pBlobLists[i].GetBlobNum()) then break; pBLIndex[i] := 0; end; // Next time shift. */ if (i = EBD_FRAME_NUM) then finish := 1; end; until finish = 1; // Check next time configuration of connected components. */ {$IFDEF 0} begin // */ WriteLn('BlobDetector configurations = ', Count); for i := 0 to EBD_FRAME_NUM - 1 do begin WriteLn(iif(Assigned(m_pBlobLists[i]), m_pBlobLists[i].GetBlobNum(), 0)); end; WriteLn; end; {$ENDIF} if (BestError <> -1) then begin // Write new blob to output and delete from blob list: */ pNewBlob := m_pBlobLists[EBD_FRAME_NUM - 1].GetBlob(pBL_BEST[EBD_FRAME_NUM - 1]); pNewBlobList.AddBlob(pNewBlob); for i := 0 to EBD_FRAME_NUM - 1 do begin // Remove blob from each list: */ m_pBlobLists[i].DelBlob(pBL_BEST[i]); end; // Remove blob from each list. */ Result := 1; end; // Write new blob to output and delete from blob list. */ end; // Analyze blob list to find best blob trajectory. */ end; procedure TCvBlobDetectorSimple.Release; begin inherited; end; { TCvBlobDetectorCC } constructor TCvBlobDetectorCC.Create; var i: Integer; begin inherited; // /*CvDrawShape shapes[] = // { // { CvDrawShape::RECT, {{255,255,255}} }, // { CvDrawShape::RECT, {{0,0,255}} }, // { CvDrawShape::ELLIPSE, {{0,255,0}} } // }; // int num_shapes = sizeof(shapes) / sizeof(shapes[0]);*/ i := 0; SEQ_SIZE := 10; AddParam('Latency', @SEQ_SIZE); for i := 0 to SEQ_SIZE_MAX - 1 do m_pBlobLists[i] := NiL; for i := 0 to SEQ_NUM - 1 do m_TrackSeq[i].size := 0; m_TrackNum := 0; m_HMin := 0.02; m_WMin := 0.01; AddParam('HMin', @m_HMin); AddParam('WMin', @m_WMin); m_MinDistToBorder := 1.1; AddParam('MinDistToBorder', @m_MinDistToBorder); CommentParam('MinDistToBorder', 'Minimal allowed distance from blob center to image border in blob sizes'); m_Clastering := 1; AddParam('Clastering', @m_Clastering); CommentParam('Clastering', 'Minimal allowed distance from blob center to image border in blob sizes'); m_param_split_detector_file_name := 0; {$IFDEF USE_OBJECT_DETECTOR} AddParam('Detector', @m_param_split_detector_file_name); CommentParam('Detector', 'Detector file name'); {$ENDIF} m_param_roi_scale := 1.5; AddParam('ROIScale', @m_param_roi_scale); CommentParam('ROIScale', 'Determines the size of search window around a blob'); m_param_only_roi := 1; AddParam('OnlyROI', @m_param_only_roi); CommentParam('OnlyROI', 'Shows the whole debug image (0) or only ROIs where the detector was applied (1)'); m_min_window_size := cvSize(0, 0); m_max_border := 0; m_roi_seq := cvCreateSeq(0, sizeof(m_roi_seq^), sizeof(TCvRect), cvCreateMemStorage()); SetModuleName('CC'); end; destructor TCvBlobDetectorCC.Destroy; Var i: Integer; begin for i := 0 to SEQ_SIZE_MAX - 1 do if Assigned(m_pBlobLists[i]) then m_pBlobLists[i].Free; if Assigned(m_roi_seq) then begin cvReleaseMemStorage(m_roi_seq^.storage); m_roi_seq := 0; end; // cvDestroyWindow( "EnteringBlobDetectionDebug" ); inherited; end; function CompareContour(const a: Pointer; const b: Pointer; userdata: Pointer): Integer; cdecl; Var dx, dy, h, w, ht, wt: Single; pa, pB: TCvPoint2D32f; ra, rb: TCvRect; pCA: pCvSeq; pCB: pCvSeq; begin pCA := pCvSeq(a); pCB := pCvSeq(b); ra := pCvContour(pCA).rect; rb := pCvContour(pCB).rect; pa.X := ra.X + ra.width * 0.5; pa.Y := ra.Y + ra.height * 0.5; pB.X := rb.X + rb.width * 0.5; pB.Y := rb.Y + rb.height * 0.5; w := (ra.width + rb.width) * 0.5; h := (ra.height + rb.height) * 0.5; dx := abs(pa.X - pB.X) - w; dy := abs(pa.Y - pB.Y) - h; // wt = MAX(ra.width,rb.width)*0.1f; wt := 0; ht := MAX(ra.height, rb.height) * 0.3; Result := Integer((dx < wt) and (dy < ht)); end; function TCvBlobDetectorCC.DetectNewBlob(pImg, pFGMask: pIplImage; pNewBlobList, pOldBlobList: TCvBlobSeq): Integer; Var S: TCvSize; i: Integer; Blobs: TCvBlobSeq; storage: pCvMemStorage; pIB: pIplImage; cnts: pCvSeq; cnt: pCvSeq; NewBlob: TCvBlob; M00, X, Y, XX, YY: double; m: TCvMoments; r: TCvRect; mat: TCvMat; pB: pCvBlob; j: Integer; pBOld: pCvBlob; N: Integer; temp: TCvBlob; AreaP, AreaN: Single; pP: pCvBlob; pN: pCvBlob; pTrack: pDefSeq; BestError: double; BestTrack: Integer; pNewBlobs: TCvBlobSeq; NewTrackNum: Integer; pBNew: pCvBlob; AsignedTrack: Integer; dx, dy: double; pLastBlob: pCvBlob; Good: Integer; k: Integer; Error: double; pBL: pCvBlob; sum, jsum, // estimated parameters of moving x(t) := a*t+b*/ a, b: array [0 .. 1] of Single; num_new_blobs: Integer; bb: pCvBlob; roi_stub: TCvMat; roi_mat: TCvMat; scaled_roi_mat: TCvMat; d_b: TCvDetectedBlob; pI: pIplImage; cnt_cur: Integer; color: TCvScalar; begin Result := 0; S := cvSize(pFGMask.width, pFGMask.height); // Shift blob list: */ begin if Assigned(m_pBlobLists[SEQ_SIZE - 1]) then m_pBlobLists[SEQ_SIZE - 1].Free; for i := SEQ_SIZE - 1 downto 1 do m_pBlobLists[i] := m_pBlobLists[i - 1]; m_pBlobLists[0] := TCvBlobSeq.Create; end; // Shift blob list. */ // Create contours and add new blobs to blob list: */ begin // Create blobs: */ storage := cvCreateMemStorage(); if (m_Clastering <> 0) then begin // Glue contours: */ cvFindBlobsByCCClasters(pFGMask, &Blobs, storage); end // Glue contours. */ else begin // */ pIB := cvCloneImage(pFGMask); cnts := nil; cnt := nil; cvThreshold(pIB, pIB, 128, 255, CV_THRESH_BINARY); cvFindContours(pIB, storage, cnts, sizeof(TCvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0)); // Process each contour: */ cnt := cnts; while Assigned(cnt) do // cnt := cnt.h_next) begin // Image moments: */ r := pCvContour(cnt).rect; if (r.height < S.height * m_HMin) or (r.width < S.width * m_WMin) then continue; CvMoments(cvGetSubRect(pFGMask, @mat, r), @m, 0); M00 := cvGetSpatialMoment(@m, 0, 0); if (M00 <= 0) then continue; X := cvGetSpatialMoment(@m, 1, 0) / M00; Y := cvGetSpatialMoment(@m, 0, 1) / M00; XX := (cvGetSpatialMoment(@m, 2, 0) / M00) - X * X; YY := (cvGetSpatialMoment(@m, 0, 2) / M00) - Y * Y; NewBlob := CvBlob(r.X + X, r.Y + Y, (4 * sqrt(XX)), (4 * sqrt(YY))); Blobs.AddBlob(@NewBlob); end; // Next contour. */ cvReleaseImage(pIB); end; // One contour - one blob. */ begin // Delete small and intersected blobs: */ for i := Blobs.GetBlobNum() downto 1 do begin pB := Blobs.GetBlob(i - 1); if (pB.h < S.height * m_HMin) or (pB.w < S.width * m_WMin) then begin Blobs.DelBlob(i - 1); continue; end; if Assigned(pOldBlobList) then begin for j := pOldBlobList.GetBlobNum() downto 1 do begin pBOld := pOldBlobList.GetBlob(j - 1); if ((abs(pBOld.X - pB.X) < (CV_BLOB_RX(pBOld) + CV_BLOB_RX(pB)))) and ((abs(pBOld.Y - pB.Y) < (CV_BLOB_RY(pBOld) + CV_BLOB_RY(pB)))) then begin // Intersection detected, delete blob from list: */ Blobs.DelBlob(i - 1); break; end; end; // Check next old blob. */ end; // if pOldBlobList. */ end; // Check next blob. */ end; // Delete small and intersected blobs. */ begin // Bubble-sort blobs by size: */ N := Blobs.GetBlobNum(); for i := 1 to N - 1 do begin for j := i downto 1 do begin pP := Blobs.GetBlob(j - 1); pN := Blobs.GetBlob(j); AreaP := CV_BLOB_WX(pP) * CV_BLOB_WY(pP); AreaN := CV_BLOB_WX(pN) * CV_BLOB_WY(pN); if (AreaN < AreaP) then break; temp := pN[0]; pN[0] := pP[0]; pP[0] := temp; end; end; // Copy only first 10 blobs: */ for i := 0 to MIN(N, 10) - 1 do begin m_pBlobLists[0].AddBlob(Blobs.GetBlob(i)); end; end; // Sort blobs by size. */ cvReleaseMemStorage(storage); end; // Create blobs. */ begin // Shift each track: */ for j := 0 to m_TrackNum - 1 do begin pTrack := @m_TrackSeq[j]; for i := SEQ_SIZE - 1 downto 1 do pTrack.pBlobs[i] := pTrack.pBlobs[i - 1]; pTrack.pBlobs[0] := nil; if (pTrack.size = SEQ_SIZE) then Dec(pTrack.size); end; end; // Shift each track. */ // Analyze blob list to find best blob trajectory: */ begin BestError := -1; BestTrack := -1;; pNewBlobs := m_pBlobLists[0]; NewTrackNum := 0; for i := pNewBlobs.GetBlobNum() downto 1 do begin pBNew := pNewBlobs.GetBlob(i - 1); AsignedTrack := 0; for j := 0 to m_TrackNum - 1 do begin pTrack := @m_TrackSeq[j]; pLastBlob := iif(pTrack.size > 0, pTrack.pBlobs[1], nil); if (pLastBlob = nil) then continue; dx := abs(CV_BLOB_X(pLastBlob) - CV_BLOB_X(pBNew)); dy := abs(CV_BLOB_Y(pLastBlob) - CV_BLOB_Y(pBNew)); if (dx > 2 * CV_BLOB_WX(pLastBlob)) or (dy > 2 * CV_BLOB_WY(pLastBlob)) then continue; Inc(AsignedTrack); if (pTrack.pBlobs[0] = nil) then begin // Fill existed track: */ pTrack.pBlobs[0] := pBNew; Inc(pTrack.size); end else if ((m_TrackNum + NewTrackNum) < SEQ_NUM) then begin // Duplicate existed track: */ m_TrackSeq[m_TrackNum + NewTrackNum] := pTrack[0]; m_TrackSeq[m_TrackNum + NewTrackNum].pBlobs[0] := pBNew; Inc(NewTrackNum); end; end; // Next track. */ if (AsignedTrack = 0) and ((m_TrackNum + NewTrackNum) < SEQ_NUM) then begin // Initialize new track: */ m_TrackSeq[m_TrackNum + NewTrackNum].size := 1; m_TrackSeq[m_TrackNum + NewTrackNum].pBlobs[0] := pBNew; Inc(NewTrackNum); end; end; // Next new blob. */ m_TrackNum := m_TrackNum + NewTrackNum; // Check each track: */ for i := 0 to m_TrackNum - 1 do begin Good := 1; pTrack := @m_TrackSeq[i]; pBNew := pTrack.pBlobs[0]; if (pTrack.size <> SEQ_SIZE) then continue; if (pBNew = nil) then continue; // Check intersection last blob with existed: */ if (Good <> 0) and Assigned(pOldBlobList) then begin for k := pOldBlobList.GetBlobNum() downto 1 do begin pBOld := pOldBlobList.GetBlob(k - 1); if ((abs(pBOld.X - pBNew.X) < (CV_BLOB_RX(pBOld) + CV_BLOB_RX(pBNew)))) and ((abs(pBOld.Y - pBNew.Y) < (CV_BLOB_RY(pBOld) + CV_BLOB_RY(pBNew)))) then Good := 0; end; end; // Check intersection last blob with existed. */ // Check distance to image border: */ if (Good <> 0) then begin // Check distance to image border: */ dx := MIN(pBNew.X, S.width - pBNew.X) / CV_BLOB_RX(pBNew); dy := MIN(pBNew.Y, S.height - pBNew.Y) / CV_BLOB_RY(pBNew); if (dx < m_MinDistToBorder) or (dy < m_MinDistToBorder) then Good := 0; end; // Check distance to image border. */ // Check uniform motion: */ if (Good <> 0) then begin // Check uniform motion: */ Error := 0; N := pTrack.size; pBL := pTrack.pBlobs[0]; sum[0] := 0; sum[1] := 0; jsum := sum; for j := 0 to N - 1 do begin X := pBL[j].X; Y := pBL[j].Y; sum[0] := sum[0] + X; jsum[0] := jsum[0] + j * X; sum[1] := sum[1] + Y; jsum[1] := jsum[1] + j * Y; end; a[0] := 6 * ((1 - N) * sum[0] + 2 * jsum[0]) / (N * (N * N - 1)); b[0] := -2 * ((1 - 2 * N) * sum[0] + 3 * jsum[0]) / (N * (N + 1)); a[1] := 6 * ((1 - N) * sum[1] + 2 * jsum[1]) / (N * (N * N - 1)); b[1] := -2 * ((1 - 2 * N) * sum[1] + 3 * jsum[1]) / (N * (N + 1)); for j := 0 to N - 1 do begin Error := Error + power(a[0] * j + b[0] - pBL[j].X, 2) + power(a[1] * j + b[1] - pBL[j].Y, 2); end; Error := sqrt(Error / N); if (Error > S.width * 0.01) or (abs(a[0]) > S.width * 0.1) or (abs(a[1]) > S.height * 0.1) then Good := 0; // New best trajectory: */ if (Good <> 0) and ((BestError = -1) or (BestError > Error)) then begin // New best trajectory: */ BestTrack := i; BestError := Error; end; // New best trajectory. */ end; // Check uniform motion. */ end; // Next track. */ {$IFDEF 0} begin // */ WriteLn('BlobDetector configurations := ', m_TrackNum); for i := 0 to SEQ_SIZE - 1 do begin WriteLn(iif(Assigned(m_pBlobLists[i]), m_pBlobLists[i].GetBlobNum(), 0)); end; WriteLn; end; {$ENDIF} if (BestTrack >= 0) then begin // Put new blob to output and delete from blob list: */ assert(m_TrackSeq[BestTrack].size = SEQ_SIZE); assert(Assigned(m_TrackSeq[BestTrack].pBlobs[0])); pNewBlobList.AddBlob(m_TrackSeq[BestTrack].pBlobs[0]); m_TrackSeq[BestTrack].pBlobs[0] := nil; Dec(m_TrackSeq[BestTrack].size); Result := 1; end; // Put new blob to output and mark in blob list to delete. */ end; // Analyze blod list to find best blob trajectory. */ begin // Delete bad tracks: */ for i := m_TrackNum - 1 downto 0 do begin // Delete bad tracks: */ if Assigned(m_TrackSeq[i].pBlobs[0]) then continue; if (m_TrackNum > 0) then begin Dec(m_TrackNum); m_TrackSeq[i] := m_TrackSeq[m_TrackNum]; end; end; // Delete bad tracks: */ end; {$IFDEF USE_OBJECT_DETECTOR} if (m_split_detector) and (pNewBlobList.GetBlobNum() > 0) then begin num_new_blobs := pNewBlobList.GetBlobNum(); if Assigned(m_roi_seq) then cvClearSeq(m_roi_seq)m_debug_blob_seq.Clear(); for i := 0 to num_new_blobs - 1 do begin b := pNewBlobList.GetBlob(i); roi_mat := nil; scaled_roi_mat := nil; d_b := CvDetectedBlob(CV_BLOB_X(b), CV_BLOB_Y(b), CV_BLOB_WX(b), CV_BLOB_WY(b), 0); m_debug_blob_seq.AddBlob(&d_b); float scale := m_param_roi_scale * m_min_window_size.height / CV_BLOB_WY(b); float b_width := MAX(CV_BLOB_WX(b), m_min_window_size.width / scale) + (m_param_roi_scale - 1.0) * (m_min_window_size.width / scale) + 2.0 F * m_max_border / scale; float b_height := CV_BLOB_WY(b) * m_param_roi_scale + 2.0 F * m_max_border / scale; CvRect roi := cvRectIntersection(CvRect(cvFloor(CV_BLOB_X(b) - 0.5 F * b_width), cvFloor(CV_BLOB_Y(b) - 0.5 F * b_height), cvCeil(b_width), cvCeil(b_height)), CvRect(0, 0, pImg.width, pImg.height)); if (roi.width < := 0) or (roi.height < := 0) then continue; if (m_roi_seq) then cvSeqPush(m_roi_seq, &roi); roi_mat := cvGetSubRect(pImg, &roi_stub, roi); scaled_roi_mat := cvCreateMat(cvCeil(scale * roi.height), cvCeil(scale * roi.width), CV_8UC3); cvResize(roi_mat, scaled_roi_mat); m_detected_blob_seq.Clear(); m_split_detector.Detect(scaled_roi_mat, &m_detected_blob_seq); cvReleaseMat(&scaled_roi_mat); for (int k := 0; k < m_detected_blob_seq.GetBlobNum(); + + k) do begin CvDetectedBlob * b := (CvDetectedBlob * )m_detected_blob_seq.GetBlob(k); // scale and shift each detected blob back to the original image coordinates */ CV_BLOB_X(b) := CV_BLOB_X(b) / scale + roi.X; CV_BLOB_Y(b) := CV_BLOB_Y(b) / scale + roi.Y; CV_BLOB_WX(b) / := scale; CV_BLOB_WY(b) / := scale; CvDetectedBlob d_b := CvDetectedBlob(CV_BLOB_X(b), CV_BLOB_Y(b), CV_BLOB_WX(b), CV_BLOB_WY(b), 1, b.response); m_debug_blob_seq.AddBlob(&d_b); end; if (m_detected_blob_seq.GetBlobNum() > 1) then begin // * Split blob. // * The original blob is replaced by the first detected blob, // * remaining detected blobs are added to the end of the sequence: // */ CvBlob * first_b := m_detected_blob_seq.GetBlob(0); CV_BLOB_X(b) := CV_BLOB_X(first_b); CV_BLOB_Y(b) := CV_BLOB_Y(first_b); CV_BLOB_WX(b) := CV_BLOB_WX(first_b); CV_BLOB_WY(b) := CV_BLOB_WY(first_b); for (int j := 1; j < m_detected_blob_seq.GetBlobNum(); + + j) do begin CvBlob * detected_b := m_detected_blob_seq.GetBlob(j); pNewBlobList.AddBlob(detected_b); end; end; end; // For each new blob. */ for (i := 0; i < pNewBlobList.GetBlobNum(); + + i) do begin CvBlob * b := pNewBlobList.GetBlob(i); CvDetectedBlob d_b := CvDetectedBlob(CV_BLOB_X(b), CV_BLOB_Y(b), CV_BLOB_WX(b), CV_BLOB_WY(b), 2); m_debug_blob_seq.AddBlob(&d_b); end; end; // if( m_split_detector ) {$ENDIF} end; procedure TCvBlobDetectorCC.ParamUpdate; begin inherited; end; procedure TCvBlobDetectorCC.Release; begin inherited; end; procedure cvFindBlobsByCCClasters(pFG: pIplImage; pBlobs: TCvBlobSeq; storage: pCvMemStorage); Var pIB: pIplImage; cnt: pCvSeq; cnt_list: pCvSeq; clasters: pCvSeq; claster_cur, claster_num: Integer; cnt_cur: Integer; NewBlob: TCvBlob; M00, X, Y, XX, YY: double; // image moments */ m: TCvMoments; rect_res: TCvRect; mat: TCvMat; rect: TCvRect; cont: pCvSeq; k: Integer; x0, x1, y0, y1: Integer; begin // Create contours: */ pIB := nil; cnt := nil; cnt_list := cvCreateSeq(0, sizeof(TCvSeq), sizeof(TCvSeq), storage); clasters := nil; pIB := cvCloneImage(pFG); cvThreshold(pIB, pIB, 128, 255, CV_THRESH_BINARY); cvFindContours(pIB, storage, cnt, sizeof(TCvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0)); cvReleaseImage(pIB); // Create cnt_list. */ // Process each contour: */ while Assigned(cnt) do begin cvSeqPush(cnt_list, cnt); cnt := cnt^.h_next; end; claster_num := cvSeqPartition(cnt_list, storage, clasters, CompareContour, nil); for claster_cur := 0 to claster_num - 1 do begin rect_res := CvRect(-1, -1, -1, -1); for cnt_cur := 0 to clasters.total - 1 do begin k := pInteger(cvGetSeqElem(clasters, cnt_cur))^; if (k <> claster_cur) then continue; cont := pCvSeq(cvGetSeqElem(cnt_list, cnt_cur)); rect := pCvContour(cont).rect; if (rect_res.height < 0) then begin rect_res := rect; end else begin // Unite rects: */ x0 := MIN(rect_res.X, rect.X); y0 := MIN(rect_res.Y, rect.Y); x1 := MAX(rect_res.X + rect_res.width, rect.X + rect.width); y1 := MAX(rect_res.Y + rect_res.height, rect.Y + rect.height); rect_res.X := x0; rect_res.Y := y0; rect_res.width := x1 - x0; rect_res.height := y1 - y0; end; end; if (rect_res.height < 1) or (rect_res.width < 1) then begin X := 0; Y := 0; XX := 0; YY := 0; end else begin CvMoments(cvGetSubRect(pFG, @mat, rect_res), @m, 0); M00 := cvGetSpatialMoment(@m, 0, 0); if (M00 <= 0) then continue; X := cvGetSpatialMoment(@m, 1, 0) / M00; Y := cvGetSpatialMoment(@m, 0, 1) / M00; XX := (cvGetSpatialMoment(@m, 2, 0) / M00) - X * X; YY := (cvGetSpatialMoment(@m, 0, 2) / M00) - Y * Y; end; NewBlob := CvBlob(rect_res.X + X, rect_res.Y + Y, (4 * sqrt(XX)), (4 * sqrt(YY))); pBlobs.AddBlob(@NewBlob); end; // Next cluster. */ {$IFDEF 0} begin // Debug info: pI := cvCreateImage(cvSize(pFG.width, pFG.height), IPL_DEPTH_8U, 3); cvZero(pI); for claster_cur := 0 to claster_num - 1 do begin color := CV_RGB(random(256), random(256), random(256)); for cnt_cur := 0 to clasters.total - 1 do begin k := pInteger(cvGetSeqElem(clasters, cnt_cur))^; if (k <> claster_cur) then continue; cnt := pCvSeq(cvGetSeqElem(cnt_list, cnt_cur)); cvDrawContours(pI, cnt, color, color, 0, 1, 8); end; pB := pBlobs.GetBlob(claster_cur); X := cvRound(CV_BLOB_RX(pB)); Y := cvRound(CV_BLOB_RY(pB)); cvEllipse(pI, cvPointFrom32f(CV_BLOB_CENTER(pB)), cvSize(MAX(1, X), MAX(1, Y)), 0, 0, 360, color, 1); end; cvNamedWindow('Clusters', 0); cvShowImage('Clusters', pI); cvReleaseImage(pI); end; // Debug info. */ {$ENDIF} end; // cvFindBlobsByCCClasters */ end.