void GSurface::calculateCoefficients(){ Transformation geometry(_geometry_matrix); Transformation geometryTransposed = geometry.transpose(); Transformation coords_x(_coords_x); _cx = (geometry * coords_x * geometryTransposed).m(); Transformation coords_y(_coords_y); _cy = (geometry * coords_y * geometryTransposed).m(); Transformation coords_z(_coords_z); _cz = (geometry * coords_z * geometryTransposed).m(); }
void CGraphPanel::DrawAxis(CDC* pDC, CRect& rect_to_draw) { int i; if (CurrentCoordsX == NULL || CurrentCoordsY == NULL) return; //create new set of CCoordinates CCoordinates coords_x(rect_to_draw.left, rect_to_draw.right, CurrentCoordsX->v1(), CurrentCoordsX->v2()); CCoordinates coords_y(rect_to_draw.bottom, rect_to_draw.top, CurrentCoordsY->v1(), CurrentCoordsY->v2()); //draw axis and zero line first CPen BlackDashPen(PS_DOT, 1, RGB(0, 0, 0)); CPen bkPen(PS_SOLID, 1, bkColor); CPen* OldPen = (CPen*)pDC->SelectObject(&BlackDashPen); pDC->SetBkMode( TRANSPARENT); //horizontal double Delta; double* wp; double* sp; int count, maxexp, NMax; NMax = hruler->GetNMax(pDC, rect_to_draw); coords_x.DivideInterval(NMax, &maxexp, &Delta, &count, &sp, &wp); for (i=0; i<count; i++) { int x = (int)sp[i]; pDC->MoveTo(x, rect_to_draw.bottom); pDC->LineTo(x, rect_to_draw.top); }; if (sp!=NULL) delete sp; if (wp!=NULL) delete wp; //vertical NMax = vruler->GetNMax(pDC, rect_to_draw); coords_y.DivideInterval(NMax, &maxexp, &Delta, &count, &sp, &wp); for (i=0; i<count; i++) { int x = (int)sp[i]; pDC->MoveTo(rect_to_draw.right,x); pDC->LineTo(rect_to_draw.left,x); }; if (sp!=NULL) delete sp; if (wp!=NULL) delete wp; pDC->SelectObject(OldPen); }
void CGraphPanel::DrawPoints(CDC* dc, CRect& rect_to_draw) { //draw all the points SSinglePoint ssp, ssp1; int x, y, x1, y1, grnum; if (CurrentCoordsX == NULL || CurrentCoordsY == NULL) return; CGraphWnd* main_wnd = get_main_graph_window(); if (main_wnd == NULL) return; //save current state of device context int saved_dc = dc->SaveDC(); //set new clipping region dc->IntersectClipRect(rect_to_draw); //create new set of CCoordinates CCoordinates coords_x(rect_to_draw.left, rect_to_draw.right, CurrentCoordsX->v1(), CurrentCoordsX->v2()); CCoordinates coords_y(rect_to_draw.bottom, rect_to_draw.top, CurrentCoordsY->v1(), CurrentCoordsY->v2()); CGraphProps* grprop = main_wnd->GetFirstGraph(&grnum); while (grprop!=NULL) { if (grprop->GetSize() ==0 || !grprop->IsVisible()) { grprop = main_wnd->GetNextGraph(&grnum); continue; }; //set color CPen* pen = grprop->GetPen(); CBrush* brush = grprop->GetBrush(); CPen* oldpen = (CPen*)dc->SelectObject(pen); CBrush* oldbrush = (CBrush*)dc->SelectObject(brush); grprop->GetPoint(0, &ssp); x = (int)coords_x.WtoX(ssp.x); y = (int)coords_y.WtoX(ssp.y); if ((long)(m_grflags & GRAPH_SQUAREPOINTS)!=0) DrawSquarePoint(dc, x, y); for (int i=1; i<grprop->GetSize(); i++) { grprop->GetPoint(i, &ssp); x1 = (int)coords_x.WtoX(ssp.x); y1 = (int)coords_y.WtoX(ssp.y); if (x != x1 || y != y1) { if ((m_grflags & GRAPH_SQUAREPOINTS)!=0) DrawSquarePoint(dc, x1, y1); if ((m_grflags & GRAPH_GRAPH_SCATTER) == 0) { dc->MoveTo(x, y); dc->LineTo(x1, y1); }; x = x1; y = y1; }; }; dc->SelectObject(oldpen); dc->SelectObject(oldbrush); grprop->ReleasePen(pen); grprop->ReleaseBrush(brush); grprop = main_wnd->GetNextGraph(&grnum); }; dc->RestoreDC(saved_dc); }
int DetectorBarcode::Detect() { std::cout << "DetectorBarcode::Detect()" << std::endl; assert(this->image_.data != NULL); bool debugstripecode = true; //@TODO MAKE SURE TO SET ME TO FALSE IN PRODUCTION bool useAdaptiveThersholding = true; int dpi = 400; //this works well for all scales and sizes.. Mat matImageK; cvtColor(this->image_, matImageK, cv::COLOR_BGR2GRAY); cv::Mat matThres; // VARIABLES // double bar_height_mm_min = 3.7; //[7.5mm=our NMNH c39] [10.7mm=NMNH cover c39] double bar_height_mm_max = 20; double bar_ar_min = 4; double bar_ar_max = 110; int min_characters = 5; //minimum characters in barcode string double bar_dist_group_mm_max = 9.0; //Maximum distance between any grouped bar to be part of the bar group // COMPUTE // double bar_height_px_min = bar_height_mm_min/25.4*dpi; double bar_height_px_max = bar_height_mm_max/25.4*dpi; double bar_area_px_min = bar_height_px_min*(bar_height_px_min*1.0/bar_ar_max); //Dont allow the area to be less than 1px row bar_area_px_min = bar_area_px_min < bar_height_px_min ? bar_height_px_min : bar_area_px_min; double bar_area_px_max = bar_height_px_max*(bar_height_px_max*1.0/bar_ar_min); double bar_dist_group_px_max = bar_dist_group_mm_max/25.4*dpi; if (useAdaptiveThersholding) { //int AT_blocksize = dpi*0.05; int AT_blocksize = bar_height_px_min*0.5; int AT_iseven=AT_blocksize%2; AT_blocksize += 1+AT_iseven; //Makes sure the blocksize is an even number //cout << "AT_blocksize=" << AT_blocksize << endl; adaptiveThreshold(matImageK, matThres, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, AT_blocksize, 20); } else { threshold(matImageK, matThres, 127, 255, THRESH_BINARY_INV); } if (debugstripecode) { //cout << "dpi=" << dpi << endl; imwrite("/Users/tzaman/Desktop/bc/matImage.tif", this->image_); imwrite("/Users/tzaman/Desktop/bc/matThres.tif", matThres); } vector< vector<Point> > contours; vector<Vec4i> hierarchy; findContours( matThres, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE ); //cout << "contours.size()=" << contours.size() << endl; if (contours.size() == 0) { string strErr = "No contours found."; cout << strErr << endl; return RET_NONE_FOUND; } //RANSAC vars int min_inliers = (min_characters+2)*5*0.75; //+2 (start&stop), *5 (stripes per char), *0.x (margin) double max_px_dist = (bar_height_px_min+bar_height_px_max)*0.5*0.05; //Maximum distance from RANSAC line to a point vector<RotatedRect> stripeCandidates; for(int i = 0; i >= 0; i = hierarchy[i][0] ) { double cArea = contourArea( contours[i],false); if (cArea < bar_area_px_min*0.5){ continue; } if (cArea > bar_area_px_max){ continue; } //cout << "[" << i << "]" << " cArea=" << cArea << endl; RotatedRect rotRect= minAreaRect(contours[i]); double ar = max(double(rotRect.size.width),double(rotRect.size.height)) / min(double(rotRect.size.width),double(rotRect.size.height)); if (ar < bar_ar_min){ continue; } if (ar > bar_ar_max){ continue; } double width = std::min(rotRect.size.width, rotRect.size.height); double height = std::max(rotRect.size.width, rotRect.size.height); //Check the length if (height < bar_height_px_min){ //Stripe too small continue; } if (height > bar_height_px_max ){ //Stripe too long continue; } //cout << i << " rotRect: sz=" << rotRect.size << " cp=" << rotRect.center << " a=" << rotRect.angle << " ar=" << ar << endl; Rect rCrop = boundingRect(contours[i]); //Below parameter is dynamic, plz note double min_area_fill=0.15;// = 0.25 ;// 0.4 means 40% of the bounding rectangle of the contour needs to be filled //The min_area_fill threshold should be dependent on the width in pixels, because there's more noise in thinner ones if (width<3){ min_area_fill = 0.05; } else if (width <5){ min_area_fill = 0.10; } //Check if the rectangle is actually filled well int fullarea = rCrop.area(); if ( (double(cArea)/double(fullarea)) < min_area_fill){ continue; } //cout << i << " fullarea=" << fullarea << " carea=" << cArea << endl; if (debugstripecode){ imwrite("/Users/tzaman/Desktop/seg/" + std::to_string(i) + ".tif", matImageK(rCrop)); } stripeCandidates.push_back(rotRect); } if (debugstripecode){ Mat matBarcodeFull = this->image_.clone(); for (int j=0; j<stripeCandidates.size(); j++){ util::rectangle(matBarcodeFull, stripeCandidates[j], cv::Scalar(255,0,0), 2); } imwrite("/Users/tzaman/Desktop/bc/_candidates.tif", matBarcodeFull); } //cout << "stripeCandidates.size()=" << stripeCandidates.size() << endl; if (stripeCandidates.size() < min_inliers){ string strErr = "Code 39 did not find enough bars to accurately make a code."; cout << strErr << endl; return RET_NONE_FOUND; } std::vector<Point> vecPtRectCenter = util::vecrotrect2vecpt(stripeCandidates); std::vector<std::vector<int> > vecGroupIdxs = util::groupPoints(vecPtRectCenter, bar_dist_group_px_max, min_inliers); //std::vector<std::vector<cv::Point> > vecGroupPts(vecGroupIdxs.size()); std::vector<std::vector<cv::RotatedRect> > vecGroupRects(vecGroupIdxs.size()); //Relate indexes to points and add to group vector for (int i=0; i<vecGroupIdxs.size(); i++){ //vecGroupPts[i].resize(vecGroupIdxs[i].size()); vecGroupRects[i].resize(vecGroupIdxs[i].size()); for (int j=0; j<vecGroupIdxs[i].size(); j++){ //cout << i << "," << j << endl; //vecGroupPts[i][j] = vecPtRectCenter[vecGroupIdxs[i][j]]; vecGroupRects[i][j] = stripeCandidates[vecGroupIdxs[i][j]]; } } //Draw all groups //if(debugstripecode){ // for (int i=0; i<vecGroupPts.size(); i++){ // Mat matGroup = matImage.clone(); // for (int j=0; j<vecGroupPts[i].size(); j++){ // circle(matGroup, vecGroupPts[i][j], 5, Scalar(255,0,255), 1, CV_AA,0); // } // imwrite("/Users/tzaman/Desktop/bc/_group_" + std::to_string(i) + ".tif", matGroup); // } //} //exit(-1); //cout << "vecGroupPts.size()=" << vecGroupPts.size() << endl; //Erase small groups //for (int i=vecGroupPts.size()-1; i>=0; i--){ // if (vecGroupPts[i].size() < min_inliers){ // //Skipping group, too small. // vecGroupIdxs.erase(vecGroupIdxs.begin()+i); // vecGroupPts.erase(vecGroupPts.begin()+i); // } //} //cout << "vecGroupPts.size()=" << vecGroupPts.size() << endl; if (vecGroupIdxs.size() == 0) { string strErr = "Code 39 failed to ransac bars in a line."; cout << strErr << endl; return RET_NONE_FOUND; } //Now cycle over the groups vector<vector<int> > vecVecInlierIdx; vector<Vec4f> vecLines; vector<int> vecFromGroup; //Keeps track of which group the vecvecInlierIdx belongs to for (int i = 0; i < vecGroupRects.size(); i++) { Ransac(vecGroupRects[i], min_inliers, max_px_dist, vecVecInlierIdx, vecLines, this->image_); vecFromGroup.resize(vecVecInlierIdx.size(), i); } if (vecLines.size() == 0) { string strErr = "Code 39 failed to ransac bars in a line."; cout << strErr << endl; return RET_NONE_FOUND; } else { //cout << "Code39 ransac succesfull" << endl; } //for (int i=0; i<vecGroupIdxs.size(); i++){ // cout << "Group " << i << " (" << vecGroupIdxs[i].size() << ") : "; // for (int j=0; j<vecGroupIdxs[i].size(); j++){ // cout << vecGroupIdxs[i][j] << " "; // } // cout << endl; //} //Convert back vecVecInlierIdx to original indices for (int i=0; i<vecVecInlierIdx.size(); i++){ //cout << "vecVecInlierIdx[" << i << "] is from group " << vecFromGroup[i] << endl; for (int j=0; j<vecVecInlierIdx[i].size(); j++){ //cout << " " << vecVecInlierIdx[i][j] << " -> " << vecGroupIdxs[vecFromGroup[i]][vecVecInlierIdx[i][j]] << endl; vecVecInlierIdx[i][j] = vecGroupIdxs[vecFromGroup[i]][vecVecInlierIdx[i][j]]; } } for (int i=0; i < vecLines.size(); i++){ int numpts = vecVecInlierIdx[i].size(); cout << "Potential barcode #" << i << " with " << numpts << " points." << endl; //double angle=atan2(vecLines[i][1],vecLines[i][0])*180/M_PI; //For some reason it clips from [-90,90] double angle_rad = atan2(vecLines[i][1],vecLines[i][0]); //For some reason it clips from [-90,90] double angle_deg = angle_rad*180.0/M_PI; //cout << " angle_deg=" << angle_deg << endl; vector<double> bar_heights(numpts); vector<double> bar_widths(numpts); vector<double> coords_x(numpts); //Loop over all found and ransac-verified stripes in this barcode vector<cv::RotatedRect> stripesVerified(numpts); for (int j=0; j < numpts; j++){ //cout << vecVecInlierIdx[i][j] << endl; //cout << "checking out stripecandidate[" << vecVecInlierIdx[i][j] << "] #" << vecVecInlierIdx[i][j] << endl; stripesVerified[j] = stripeCandidates[vecVecInlierIdx[i][j]]; double dim_smallest = min(stripesVerified[j].size.width, stripesVerified[j].size.height); //For rotation invariance double dim_tallest = max(stripesVerified[j].size.width, stripesVerified[j].size.height); //For rotation invariance bar_heights[j] = dim_tallest; bar_widths[j] = dim_smallest; //Rotate the points straight Point2f ptRot = util::rotatePoint(stripesVerified[j].center, Point(matImageK.cols, matImageK.rows), angle_rad); //cout << ptRot << endl; coords_x[j] = ptRot.x; } double height_median = util::calcMedian(bar_heights); double width_mean = util::calcMean(bar_widths); //cout << "height_median=" << height_median <<" width_mean=" << width_mean << endl; //Find the start and end position for reading vector<size_t> coords_sorted_index; vector<double> coords_x_sorted; sort(coords_x, coords_x_sorted, coords_sorted_index); //cout << coords_x_sorted[0] << " -> " << coords_x_sorted[coords_x_sorted.size()-1] << endl; //Get extrema-stripes Point2f pt_stripe_left = stripeCandidates[vecVecInlierIdx[i][coords_sorted_index[0]]].center; Point2f pt_stripe_right = stripeCandidates[vecVecInlierIdx[i][coords_sorted_index[coords_sorted_index.size() - 1]]].center; //cout << "pt_stripe_left=" << pt_stripe_left << endl; //cout << "pt_stripe_right=" << pt_stripe_right << endl; Point2f pt_barcode_center = (pt_stripe_left + pt_stripe_right) * 0.5; //cout << "pt_barcode_center=" << pt_barcode_center << endl; //Calculate width of the barcode double barcode_width = util::pointDist(pt_stripe_left, pt_stripe_right); //cout << "barcode_width=" << barcode_width << endl; //Make the rotated rectangle around the barcode cv::RotatedRect rotrect_candidate(pt_barcode_center, Size2f(barcode_width, height_median), angle_deg); const double add_width_on_sides_before_decoding = 7.0; // this number will be multiplied by average bar width //Add margin (of a few median widths) rotrect_candidate.size += Size2f(width_mean * add_width_on_sides_before_decoding, 0); const double height_retrainer_for_collapse = 0.25; //Extract the barcode itself cv::RotatedRect rotrect_candidate_thin = rotrect_candidate; //Crop off some margin in thickness because we dont want to collapse the entire barcode. if (rotrect_candidate_thin.size.width < rotrect_candidate_thin.size.height) { rotrect_candidate_thin.size.width *= height_retrainer_for_collapse; } else { rotrect_candidate_thin.size.height *= height_retrainer_for_collapse; } openbarcode::code code_candidate; code_candidate.rotrect = rotrect_candidate_thin; this->code_candidates_.push_back(code_candidate); } return RET_SUCCESS; }