//Main tracking Algorithm void AnalysisModule::larvaFind(uchar * img, int imWidth, int imHeight, int frameInd){ input = cv::Mat(imHeight,imWidth,CV_8UC1,NULL); input.data = img; if(output.rows != imHeight | output.cols != imWidth) output.create(imHeight,imWidth,CV_8UC1); int nextInd = (index+1)%sampleInd.size(); //for Profiling tic(); sampleInd[nextInd] = frameInd; sampleTime[nextInd] = frameInd * frameIntervalMS; //On first image, automatically determine threshold level using the Otsu method // Minimizes within group variance of thresholded classes. Should land on the best boundary between backlight and larva if(index == -1) threshold = otsuThreshold(img,imWidth*imHeight); //Can speed this up by applying to a roi bounding box a bit larger than the previous one //Simple inverted binary threshold of the image cv::threshold(input,output,threshold,255,CV_THRESH_BINARY_INV); profile[0] = toctic(); //Detect Contours in the binary image cv::findContours(output,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE); profile[1] = toctic(); //No contours detected if (contours.size() == 0) { return; } //find contour with largest perimeter length double maxLen = 0; int maxInd = -1; double cLen; for(int i=0; i<contours.size(); i++){ cLen = cv::arcLength(cv::Mat(contours[i]), false); if(cLen >= maxLen){ maxLen = cLen; maxInd = i; }; } //Check to make sure that the perimeter is a larva by simple size analysis //(larva should have a certain perimeter length at 8.1um/pixel) cLarva[nextInd] = contours[maxInd]; //calculate bounding box bBox[nextInd] = cv::boundingRect(cv::Mat(cLarva[nextInd])); profile[2] = toctic(); //Calculate fourier coefficients fourierDecompose(cLarva[nextInd],nFourier,fourier[nextInd]); centroid[nextInd] = cv::Point2f(fourier[nextInd][0][AX],fourier[nextInd][0][AY]); profile[3] = toctic(); //Reconstruct the estimated boundary fourierReconstruct(fourier[nextInd],cFit,fitRes); profile[4] = toctic(); //Calculate Curvature perimeterCurvature(cFit,curve,fitRes/8); profile[5] = toctic(); //Find head and tail based on curvature minimums (small angle = sharp region) findHeadTail(cFit,curve,headTail); head[nextInd] = headTail[0]; tail[nextInd] = headTail[1]; profile[6] = toctic(); //Calculate Skeleton skeletonCalc(cFit,skeleton,headTail,length[nextInd],neck[nextInd]); profile[7] = toctic(); //Calculate bearing and head angle to bearing bodyAngles(tailBearingAngle[nextInd], headToBodyAngle[nextInd], head[nextInd], neck[nextInd], tail[nextInd]); profile[8] = toctic(); //Capture stage position stagePos[nextInd] = cv::Point(gui->stageThread->xpos,gui->stageThread->ypos); //Keep track of entire history with a sample every 30 frames if((nextInd % 30) == 0){ fullTrack[(fullTrackInd+1)%fullTrack.size()].x = stagePos[nextInd].x/gui->stageThread->tickPerMM_X+centroid[nextInd].x*gui->camThread->umPerPixel/1000.0; fullTrack[(fullTrackInd+1)%fullTrack.size()].y = stagePos[nextInd].y/gui->stageThread->tickPerMM_Y+centroid[nextInd].y*gui->camThread->umPerPixel/1000.0; fullTrackStim[(fullTrackInd+1)%fullTrack.size()] = binStimMax; binStimMax = 0; //updated from stimThread fullTrackInd++; } //Calculate Velocities of head and tail calcVelocities(nextInd); //Spew out profiling info //for(int i=0; i<9; i++) qDebug("%d: %.4fms",i,profile[i]*1000); //qDebug("\n"); index++; };
// Заполнение src->edgeA, src->edgeB src->inCont src->centLine int correctContour(frGeomStrip* src){ if(!src->isStrip()){ return -1; } src->minX = src->maxX = src->minY = src->maxY = -1; // до начала имнимум-максимум - первая точка src->minX = src->maxX = ((CvPoint*) cvGetSeqElem(src->stripCont, 0))->x; src->minY = src->maxY = ((CvPoint*) cvGetSeqElem(src->stripCont, 0))->y; // ищем координаты прямоугольника, описывающего контур for( int i=0; i<src->stripCont->total; ++i ) { CvPoint* t = (CvPoint*)cvGetSeqElem ( src->stripCont, i ); src->minX = (t->x < src->minX) ? t->x : src->minX; src->maxX = (t->x > src->maxX) ? t->x : src->maxX; src->minY = (t->y < src->minY) ? t->y : src->minY; src->maxY = (t->y > src->maxY) ? t->y : src->maxY; } // creating black image with contour highlited with white pixels // вспомогательное изображение для отрисовки и "прострела" контура IplImage* auxiliaryImage = cvCreateImage( cvSize(src->stripFrame->width, src->stripFrame->height), IPL_DEPTH_8U, 1); cvFillImage(auxiliaryImage, 0.0); CvRect myROI = src->getROI(); /* if( myROI.x > 0) printf("*"); */ // изображение для визуализации работы с гранями IplImage* visualImage = cvCreateImage( cvSize(myROI.width , myROI.height), IPL_DEPTH_8U, 3); cvFillImage(visualImage, -10.0); //сохранили оригинальное РОИ CvRect origROI = cvGetImageROI(src->stripFrame ); // назначаем новое РОИ, чтобы скопировалась только нужная область в картинку visualImage cvSetImageROI(src->stripFrame, myROI); // cvCopy(src->stripFrame, visualImage, 0); //cvShowImage("visual", visualImage); // восстановили оригинаьное РОИ cvSetImageROI(src->stripFrame, origROI); cvDrawContours(auxiliaryImage, src->stripCont, CVX_WHITE, CVX_WHITE, 0, 1); vector<CvPoint> APoints, BPoints, middlePoints; // левые и правые (иногда верхние и нижние) точки границы. заполняются сверху-вниз полосы int start = (vertical) ? src->minY + (src->maxY - src->minY) * ((float)HEAD_TAIL_PERCENT/100) : src->minX + (src->maxX - src->minX) * ((float)HEAD_TAIL_PERCENT/100) ; int finish =(vertical) ? src->minY + (src->maxY - src->minY) * (1 - (float)HEAD_TAIL_PERCENT/100) : src->minX + (src->maxX - src->minX) * (1 - (float)HEAD_TAIL_PERCENT/100) ; int begin = (vertical) ? src->minX - 1 : src->minY - 1; int end = (vertical) ? src->maxX + 1 : src->maxY + 1; for( int i = start; i < finish; i++){ // Обходим сверху вниз (л-п) for( int j = begin; j < end; j++){ // "стреляем" слева (сверху) до первого не чёрного пикселя uchar pix = (vertical) ? px(auxiliaryImage, j /*- myROI.x*/, i /*- myROI.y*/) : px(auxiliaryImage, i /*- myROI.x*/, j /*- myROI.y*/); if( 0 != pix ){ // Первая не чёрная точка в этой строке пикселов CvPoint t; t.x = (vertical) ? j : i; t.y = (vertical) ? i : j; APoints.push_back( t ); break; }// if }// for j for( int j = end; j > begin; j--){ // "стреляем" справа (снизу) до первого не чёрного пикселя uchar pix = (vertical) ? px(auxiliaryImage, j /*- myROI.x*/, i /*- myROI.y*/) : px(auxiliaryImage, i /*- myROI.x*/, j /*- myROI.y*/); if( 0 != pix ){ // Первая не чёрная точка в этой строке пикселов CvPoint t; t.x = (vertical) ? j : i; t.y = (vertical) ? i : j; BPoints.push_back( t ); break; }// if }// for j CvPoint t; t.x = (vertical) ? (APoints.back().x + BPoints.back().x) / 2 : i; t.y = (vertical) ? i : (APoints.back().y + BPoints.back().y) / 2; middlePoints.push_back(t); }// for i vector<CvPoint>::iterator itr; itr = APoints.begin(); while(itr != APoints.end()){ src->edgeA.push_back(*itr); ++itr; } itr = BPoints.begin(); while(itr != BPoints.end()){ src->edgeB.push_back(*itr); ++itr; } itr = middlePoints.begin(); while(itr != middlePoints.end()){ src->centLine.push_back(*itr); ++itr; } // smooth contours edgeA and edgeB smoothContour( src ); src->chordA = chorda(src->edgeA, src->posChordA); src->chordB = -chorda(src->edgeB, src->posChordB); // вывод результата (две грани и центральная линия) на картинку visualImage // рисуем левую, правую и среднюю линии //printf("S: %d\t", curFrStrip.isStrip() ? 1 : 0); if(src->edgeA.size() > 5){ vector<CvPoint>::iterator itr2 = src->edgeA.begin(); while(itr2 != src->edgeA.end()){ cvLine( visualImage, cvPoint(itr2->x - myROI.x, itr2->y - myROI.y), cvPoint(itr2->x - myROI.x, itr2->y - myROI.y), CV_RGB(0,0,0)); ++itr2; } vector<CvPoint>::iterator itr3 = src->edgeB.begin(); while(itr3 != src->edgeB.end()){ cvLine( visualImage, cvPoint(itr3->x - myROI.x, itr3->y - myROI.y), cvPoint(itr3->x - myROI.x, itr3->y - myROI.y), CV_RGB(0,0,0)); ++itr3; } vector<CvPoint>::iterator itr4 = src->centLine.begin(); while(itr4 != src->centLine.end()){ cvLine( visualImage, cvPoint(itr4->x - myROI.x, itr4->y - myROI.y), cvPoint(itr4->x - myROI.x, itr4->y - myROI.y), CV_RGB(0,255,0), 2); ++itr4; } } FILE* fi; #ifdef __UNDIST_PROC__ if ( src->undistAv == false ) { #endif #ifdef __DIST_PROC__ fi = fopen("chords.txt", "a+"); #endif #ifdef __UNDIST_PROC__ } else { fi = fopen("undchords.txt", "a+"); } #endif CvPoint A1, A2, B1, B2, Ah, Bh; int p1 = (src->edgeA.size()*O_o > H_T_PIX) ? src->edgeA.size()*O_o : H_T_PIX; int p2 = (src->edgeA.size()*(1-O_o) < (src->edgeA.size() - H_T_PIX)) ? src->edgeA.size()*(1-O_o) : (src->edgeA.size() - H_T_PIX); A1 = cvPoint(src->edgeA[p1].x, src->edgeA[p1].y); A2 = cvPoint(src->edgeA[p2].x, src->edgeA[p2].y); B1 = cvPoint(src->edgeB[p1].x, src->edgeB[p1].y); B2 = cvPoint(src->edgeB[p2].x, src->edgeB[p2].y); // Рисование хорд cvLine(visualImage, cvPoint(A1.x - myROI.x, A1.y - myROI.y) , cvPoint(A2.x - myROI.x, A2.y - myROI.y) , CV_RGB(255,0,0)); cvLine(visualImage, cvPoint(A1.x - myROI.x, A1.y - myROI.y-3) , cvPoint(A1.x - myROI.x, A1.y - myROI.y+3) , CV_RGB(255,0,0)); cvLine(visualImage, cvPoint(A2.x - myROI.x, A2.y - myROI.y-3) , cvPoint(A2.x - myROI.x, A2.y - myROI.y+3) , CV_RGB(255,0,0)); cvLine(visualImage, cvPoint(B1.x - myROI.x, B1.y - myROI.y) , cvPoint(B2.x - myROI.x, B2.y - myROI.y) , CV_RGB(255,0,0)); cvLine(visualImage, cvPoint(B1.x - myROI.x, B1.y - myROI.y-3) , cvPoint(B1.x - myROI.x, B1.y - myROI.y+3) , CV_RGB(255,0,0)); cvLine(visualImage, cvPoint(B2.x - myROI.x, B2.y - myROI.y-3) , cvPoint(B2.x - myROI.x, B2.y - myROI.y+3) , CV_RGB(255,0,0)); /* cvCircle(visualImage, cvPoint(src->posChordA.x - myROI.x, src->posChordA.y - myROI.y), 2, CV_RGB(255,0,0), 1); cvCircle(visualImage, cvPoint(src->posChordB.x - myROI.x, src->posChordB.y - myROI.y), 2, CV_RGB(255,0,0), 1); */ Ah = heightBase(A1, A2, src->posChordA); Bh = heightBase(B1, B2, src->posChordB); //cvDrawLine cvLine(visualImage, cvPoint(Ah.x - myROI.x, Ah.y - myROI.y), cvPoint(src->posChordA.x - myROI.x, src->posChordA.y - myROI.y), CV_RGB(0,0,0) ); cvLine(visualImage, cvPoint(Bh.x - myROI.x, Bh.y - myROI.y), cvPoint(src->posChordB.x - myROI.x, src->posChordB.y - myROI.y), CV_RGB(0,0,0) ); src->head_tail = findHeadTail(src); printf( "HT:\t%d\n", src->head_tail ); //################################# AG code#################### //chords in mm src->chordAmm = chordmm(src->edgeA, src->posChordA, src->cAnch); src->chordBmm = -chordmm(src->edgeB, src->posChordB, src->cAnch); printf("in mm:\tchords\t%3.2f\t%3.2f\tavg:\t%4.3f\tlen:\t%d\n", src->chordAmm, src->chordBmm, (src->chordAmm + src->chordBmm)/2, /*abs*/(pi2mm(src->edgeA.back(), src->cAnch ).x - pi2mm( src->edgeA.front(), src->cAnch ).x)) ; fprintf(fi,"in mm:\tchords\t%3.2f\t%3.2f\tavg:\t%4.3f\tlen:\t%d\tHT:%d\n", src->chordAmm, src->chordBmm, (src->chordAmm + src->chordBmm)/2, abs(pi2mm(src->edgeA.back(), src->cAnch ).x - pi2mm( src->edgeA.front(), src->cAnch ).x), src->head_tail) ; //################################# noneAG code#################### //################################# AG code#################### //chords in mm src->chordAmm = chordmm(src->edgeA, src->posChordA, src->cAnch); src->chordBmm = -chordmm(src->edgeB, src->posChordB, src->cAnch); printf("in mm:\tchords\t%3.2f\t%3.2f\tavg:\t%4.3f\tlen:\t%d\n", src->chordAmm, src->chordBmm, (src->chordAmm + src->chordBmm)/2, abs(pi2mm(src->edgeA.back(), src->cAnch ).x - pi2mm( src->edgeA.front(), src->cAnch ).x)) ; fprintf(fi,"in mm:\tchords\t%3.2f\t%3.2f\tavg:\t%4.3f\tlen:\t%d\n", src->chordAmm, src->chordBmm, (src->chordAmm + src->chordBmm)/2, abs(pi2mm(src->edgeA.back(), src->cAnch ).x - pi2mm( src->edgeA.front(), src->cAnch ).x)) ; //################################# noneAG code#################### //printf("chords %3.2f:%3.2f; avg: %4.3f; len: %d \n", src->chordA, src->chordB, (src->chordA + src->chordB)/2, abs(src->edgeA.back().x - src->edgeA.front().x) ); #ifdef __DEBUG_CHORDS__ fprintf(fi,"in pi:\tchords\t%3.2f\t%3.2f\tavg:\t%4.3f\tlen:\t%d\n", src->chordA, src->chordB, (src->chordA + src->chordB)/2, abs(src->edgeA.back().x - src->edgeA.front().x)) ; printf("in pi:\tchords\t%3.2f\t%3.2f\tavg:\t%4.3f\tlen:\t%d\n", src->chordA, src->chordB, (src->chordA + src->chordB)/2, abs(src->edgeA.back().x - src->edgeA.front().x)) ; #endif fclose(fi); ////////////////////////////////////// #ifdef __UNDIST_PROC__ if ( src->undistAv == false ) { #endif #ifdef __DIST_PROC__ cvShowImage("visual", visualImage); // cvShowImage("visual", auxiliaryImage); #endif #ifdef __UNDIST_PROC__ } else { cvShowImage("undVisual", visualImage); } #endif // removing binaryImage from memory cvReleaseImage(&auxiliaryImage); cvReleaseImage(&visualImage); return 0; }