cv::Rect MotionDetector::MotionDetect(cv::Mat* frame) //Detect motion and create ONE recangle that contains all the detected motion { std::vector<std::vector<cv::Point> > contours; std::vector<cv::Vec4i> hierarchy; cv::Rect bounding_rect; std::vector<cv::Rect> rects; cv::Rect largest_rect, rect_temp; // Detect motion pMOG2->operator()(*frame, fgMaskMOG2, -1); //Remove noise cv::erode(fgMaskMOG2, fgMaskMOG2, getStructuringElement(cv::MORPH_RECT, cv::Size(6, 6))); // Find the contours of motion areas in the image findContours(fgMaskMOG2, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE); // Find the bounding rectangles of the areas of motion if (contours.size() > 0) { for (int i = 0; i < contours.size(); i++) { bounding_rect = boundingRect(contours[i]); rects.push_back(bounding_rect); } // Determine the overall area with motion. largest_rect = rects[0]; for (int i = 1; i < rects.size(); i++) { rect_temp.x = min(largest_rect.x,rects[i].x); rect_temp.y = min(largest_rect.y,rects[i].y); rect_temp.width = max(largest_rect.x + largest_rect.width, rects[i].x + rects[i].width)-rect_temp.x; rect_temp.height = max(largest_rect.y + largest_rect.height, rects[i].y + rects[i].height) - rect_temp.y; largest_rect = rect_temp; } rectangle(*frame, rect_temp, cv::Scalar(0, 255, 0), 1, 8, 0); } else { largest_rect.x = 0; largest_rect.y = 0; largest_rect.width = 0; largest_rect.height = 0; } // imshow("Motion detect", fgMaskMOG2); return expandRect(largest_rect, 0, 0, frame->cols, frame->rows); }
// Checks if the provided region is partially covered by the mask // If so, it is disqualified bool DetectorMask::region_is_masked(cv::Rect region) { int MIN_WHITENESS = 248; // If the mean pixel value over the crop is very white (e.g., > 253 out of 255) // then this is in the white area of the mask and we'll use it // Make sure the region doesn't extend beyond the bounds of our image expandRect(region, 0, 0, resized_mask.cols, resized_mask.rows); Mat mask_crop = resized_mask(region); double mean_value = mean(mask_crop)[0]; if (config->debugDetector) { cout << "region_is_masked: Mean whiteness: " << mean_value << endl; } return mean_value < MIN_WHITENESS; }
void OCR::performOCR(PipelineData* pipeline_data) { const int SPACE_CHAR_CODE = 32; timespec startTime; getTimeMonotonic(&startTime); postProcessor.clear(); // Don't waste time on OCR processing if it is impossible to get sufficient characters int total_char_spaces = 0; for (unsigned int i = 0; i < pipeline_data->charRegions.size(); i++) total_char_spaces += pipeline_data->charRegions[i].size(); if (total_char_spaces < config->postProcessMinCharacters) { pipeline_data->disqualify_reason = "Insufficient character boxes detected. No OCR performed."; pipeline_data->disqualified = true; return; } for (unsigned int i = 0; i < pipeline_data->thresholds.size(); i++) { // Make it black text on white background bitwise_not(pipeline_data->thresholds[i], pipeline_data->thresholds[i]); tesseract.SetImage((uchar*) pipeline_data->thresholds[i].data, pipeline_data->thresholds[i].size().width, pipeline_data->thresholds[i].size().height, pipeline_data->thresholds[i].channels(), pipeline_data->thresholds[i].step1()); int absolute_charpos = 0; for (unsigned int line_idx = 0; line_idx < pipeline_data->charRegions.size(); line_idx++) { for (unsigned int j = 0; j < pipeline_data->charRegions[line_idx].size(); j++) { Rect expandedRegion = expandRect( pipeline_data->charRegions[line_idx][j], 2, 2, pipeline_data->thresholds[i].cols, pipeline_data->thresholds[i].rows) ; tesseract.SetRectangle(expandedRegion.x, expandedRegion.y, expandedRegion.width, expandedRegion.height); tesseract.Recognize(NULL); tesseract::ResultIterator* ri = tesseract.GetIterator(); tesseract::PageIteratorLevel level = tesseract::RIL_SYMBOL; do { const char* symbol = ri->GetUTF8Text(level); float conf = ri->Confidence(level); bool dontcare; int fontindex = 0; int pointsize = 0; const char* fontName = ri->WordFontAttributes(&dontcare, &dontcare, &dontcare, &dontcare, &dontcare, &dontcare, &pointsize, &fontindex); // Ignore NULL pointers, spaces, and characters that are way too small to be valid if(symbol != 0 && symbol[0] != SPACE_CHAR_CODE && pointsize >= config->ocrMinFontSize) { postProcessor.addLetter(string(symbol), line_idx, absolute_charpos, conf); if (this->config->debugOcr) printf("charpos%d line%d: threshold %d: symbol %s, conf: %f font: %s (index %d) size %dpx\n", absolute_charpos, line_idx, i, symbol, conf, fontName, fontindex, pointsize); bool indent = false; tesseract::ChoiceIterator ci(*ri); do { const char* choice = ci.GetUTF8Text(); //1/17/2016 adt adding check to avoid double adding same character if ci is same as symbol. Otherwise first choice will get doubled boost when choiceIterator run. if (string(symbol) != string(choice)) postProcessor.addLetter(string(choice), line_idx, absolute_charpos, ci.Confidence()); if (this->config->debugOcr) { if (indent) printf("\t\t "); printf("\t- "); printf("%s conf: %f\n", choice, ci.Confidence()); } indent = true; } while(ci.Next()); } if (this->config->debugOcr) printf("---------------------------------------------\n"); delete[] symbol; } while((ri->Next(level))); delete ri; absolute_charpos++; } } } if (config->debugTiming) { timespec endTime; getTimeMonotonic(&endTime); cout << "OCR Time: " << diffclock(startTime, endTime) << "ms." << endl; } }
AlprFullDetails AlprImpl::recognizeFullDetails(cv::Mat img, std::vector<cv::Rect> regionsOfInterest) { timespec startTime; getTimeMonotonic(&startTime); AlprFullDetails response; response.results.epoch_time = getEpochTimeMs(); response.results.img_width = img.cols; response.results.img_height = img.rows; // Fix regions of interest in case they extend beyond the bounds of the image for (unsigned int i = 0; i < regionsOfInterest.size(); i++) regionsOfInterest[i] = expandRect(regionsOfInterest[i], 0, 0, img.cols, img.rows); for (unsigned int i = 0; i < regionsOfInterest.size(); i++) { response.results.regionsOfInterest.push_back(AlprRegionOfInterest(regionsOfInterest[i].x, regionsOfInterest[i].y, regionsOfInterest[i].width, regionsOfInterest[i].height)); } if (!img.data) { // Invalid image if (this->config->debugGeneral) std::cerr << "Invalid image" << std::endl; return response; } // Convert image to grayscale if required Mat grayImg = img; if (img.channels() > 2) cvtColor( img, grayImg, CV_BGR2GRAY ); // Prewarp the image and ROIs if configured] std::vector<cv::Rect> warpedRegionsOfInterest = regionsOfInterest; // Warp the image if prewarp is provided grayImg = prewarp->warpImage(grayImg); warpedRegionsOfInterest = prewarp->projectRects(regionsOfInterest, grayImg.cols, grayImg.rows, false); vector<PlateRegion> warpedPlateRegions; // Find all the candidate regions if (config->skipDetection == false) { warpedPlateRegions = plateDetector->detect(grayImg, warpedRegionsOfInterest); } else { // They have elected to skip plate detection. Instead, return a list of plate regions // based on their regions of interest for (unsigned int i = 0; i < warpedRegionsOfInterest.size(); i++) { PlateRegion pr; pr.rect = cv::Rect(warpedRegionsOfInterest[i]); warpedPlateRegions.push_back(pr); } } queue<PlateRegion> plateQueue; for (unsigned int i = 0; i < warpedPlateRegions.size(); i++) plateQueue.push(warpedPlateRegions[i]); int platecount = 0; while(!plateQueue.empty()) { PlateRegion plateRegion = plateQueue.front(); plateQueue.pop(); PipelineData pipeline_data(img, grayImg, plateRegion.rect, config); pipeline_data.prewarp = prewarp; timespec platestarttime; getTimeMonotonic(&platestarttime); LicensePlateCandidate lp(&pipeline_data); lp.recognize(); bool plateDetected = false; if (!pipeline_data.disqualified) { AlprPlateResult plateResult; plateResult.region = defaultRegion; plateResult.regionConfidence = 0; plateResult.plate_index = platecount++; // If using prewarp, remap the plate corners to the original image vector<Point2f> cornerPoints = pipeline_data.plate_corners; cornerPoints = prewarp->projectPoints(cornerPoints, true); for (int pointidx = 0; pointidx < 4; pointidx++) { plateResult.plate_points[pointidx].x = (int) cornerPoints[pointidx].x; plateResult.plate_points[pointidx].y = (int) cornerPoints[pointidx].y; } if (detectRegion) { std::vector<StateCandidate> state_candidates = stateDetector->detect(pipeline_data.color_deskewed.data, pipeline_data.color_deskewed.elemSize(), pipeline_data.color_deskewed.cols, pipeline_data.color_deskewed.rows); if (state_candidates.size() > 0) { plateResult.region = state_candidates[0].state_code; plateResult.regionConfidence = (int) state_candidates[0].confidence; } } if (plateResult.region.length() > 0 && ocr->postProcessor.regionIsValid(plateResult.region) == false) { std::cerr << "Invalid pattern provided: " << plateResult.region << std::endl; std::cerr << "Valid patterns are located in the " << config->country << ".patterns file" << std::endl; } ocr->performOCR(&pipeline_data); ocr->postProcessor.analyze(plateResult.region, topN); timespec resultsStartTime; getTimeMonotonic(&resultsStartTime); const vector<PPResult> ppResults = ocr->postProcessor.getResults(); int bestPlateIndex = 0; cv::Mat charTransformMatrix = getCharacterTransformMatrix(&pipeline_data); for (unsigned int pp = 0; pp < ppResults.size(); pp++) { // Set our "best plate" match to either the first entry, or the first entry with a postprocessor template match if (bestPlateIndex == 0 && ppResults[pp].matchesTemplate) bestPlateIndex = plateResult.topNPlates.size(); AlprPlate aplate; aplate.characters = ppResults[pp].letters; aplate.overall_confidence = ppResults[pp].totalscore; aplate.matches_template = ppResults[pp].matchesTemplate; // Grab detailed results for each character for (unsigned int c_idx = 0; c_idx < ppResults[pp].letter_details.size(); c_idx++) { AlprChar character_details; character_details.character = ppResults[pp].letter_details[c_idx].letter; character_details.confidence = ppResults[pp].letter_details[c_idx].totalscore; cv::Rect char_rect = pipeline_data.charRegions[ppResults[pp].letter_details[c_idx].charposition]; std::vector<AlprCoordinate> charpoints = getCharacterPoints(char_rect, charTransformMatrix ); for (int cpt = 0; cpt < 4; cpt++) character_details.corners[cpt] = charpoints[cpt]; aplate.character_details.push_back(character_details); } plateResult.topNPlates.push_back(aplate); } if (plateResult.topNPlates.size() > bestPlateIndex) { AlprPlate bestPlate; bestPlate.characters = plateResult.topNPlates[bestPlateIndex].characters; bestPlate.matches_template = plateResult.topNPlates[bestPlateIndex].matches_template; bestPlate.overall_confidence = plateResult.topNPlates[bestPlateIndex].overall_confidence; bestPlate.character_details = plateResult.topNPlates[bestPlateIndex].character_details; plateResult.bestPlate = bestPlate; } timespec plateEndTime; getTimeMonotonic(&plateEndTime); plateResult.processing_time_ms = diffclock(platestarttime, plateEndTime); if (config->debugTiming) { cout << "Result Generation Time: " << diffclock(resultsStartTime, plateEndTime) << "ms." << endl; } if (plateResult.topNPlates.size() > 0) { plateDetected = true; response.results.plates.push_back(plateResult); } } if (!plateDetected) { // Not a valid plate // Check if this plate has any children, if so, send them back up for processing for (unsigned int childidx = 0; childidx < plateRegion.children.size(); childidx++) { plateQueue.push(plateRegion.children[childidx]); } } } // Unwarp plate regions if necessary prewarp->projectPlateRegions(warpedPlateRegions, grayImg.cols, grayImg.rows, true); response.plateRegions = warpedPlateRegions; timespec endTime; getTimeMonotonic(&endTime); response.results.total_processing_time_ms = diffclock(startTime, endTime); if (config->debugTiming) { cout << "Total Time to process image: " << diffclock(startTime, endTime) << "ms." << endl; } if (config->debugGeneral && config->debugShowImages) { for (unsigned int i = 0; i < regionsOfInterest.size(); i++) { rectangle(img, regionsOfInterest[i], Scalar(0,255,0), 2); } for (unsigned int i = 0; i < response.plateRegions.size(); i++) { rectangle(img, response.plateRegions[i].rect, Scalar(0, 0, 255), 2); } for (unsigned int i = 0; i < response.results.plates.size(); i++) { // Draw a box around the license plate for (int z = 0; z < 4; z++) { AlprCoordinate* coords = response.results.plates[i].plate_points; Point p1(coords[z].x, coords[z].y); Point p2(coords[(z + 1) % 4].x, coords[(z + 1) % 4].y); line(img, p1, p2, Scalar(255,0,255), 2); } // Draw the individual character boxes for (int q = 0; q < response.results.plates[i].bestPlate.character_details.size(); q++) { AlprChar details = response.results.plates[i].bestPlate.character_details[q]; line(img, Point(details.corners[0].x, details.corners[0].y), Point(details.corners[1].x, details.corners[1].y), Scalar(0,255,0), 1); line(img, Point(details.corners[1].x, details.corners[1].y), Point(details.corners[2].x, details.corners[2].y), Scalar(0,255,0), 1); line(img, Point(details.corners[2].x, details.corners[2].y), Point(details.corners[3].x, details.corners[3].y), Scalar(0,255,0), 1); line(img, Point(details.corners[3].x, details.corners[3].y), Point(details.corners[0].x, details.corners[0].y), Scalar(0,255,0), 1); } } displayImage(config, "Main Image", img); // Sleep 1ms sleep_ms(1); } if (config->debugPauseOnFrame) { // Pause indefinitely until they press a key while ((char) cv::waitKey(50) == -1) {} } return response; }
vector<PlateRegion> DetectorMorph::detect(Mat frame, std::vector<cv::Rect> regionsOfInterest) { Mat frame_gray,frame_gray_cp; if (frame.channels() > 2) { cvtColor( frame, frame_gray, CV_BGR2GRAY ); } else { frame.copyTo(frame_gray); } frame_gray.copyTo(frame_gray_cp); blur(frame_gray, frame_gray, Size(5, 5)); vector<PlateRegion> detectedRegions; for (int i = 0; i < regionsOfInterest.size(); i++) { Mat img_open, img_result; Mat element = getStructuringElement(MORPH_RECT, Size(30, 4)); morphologyEx(frame_gray, img_open, CV_MOP_OPEN, element, cv::Point(-1, -1)); img_result = frame_gray - img_open; if (config->debugDetector && config->debugShowImages) { imshow("Opening", img_result); } //threshold image using otsu thresholding Mat img_threshold, img_open2; threshold(img_result, img_threshold, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY); if (config->debugDetector && config->debugShowImages) { imshow("Threshold Detector", img_threshold); } Mat diamond(5, 5, CV_8U, cv::Scalar(1)); diamond.at<uchar>(0, 0) = 0; diamond.at<uchar>(0, 1) = 0; diamond.at<uchar>(1, 0) = 0; diamond.at<uchar>(4, 4) = 0; diamond.at<uchar>(3, 4) = 0; diamond.at<uchar>(4, 3) = 0; diamond.at<uchar>(4, 0) = 0; diamond.at<uchar>(4, 1) = 0; diamond.at<uchar>(3, 0) = 0; diamond.at<uchar>(0, 4) = 0; diamond.at<uchar>(0, 3) = 0; diamond.at<uchar>(1, 4) = 0; morphologyEx(img_threshold, img_open2, CV_MOP_OPEN, diamond, cv::Point(-1, -1)); Mat rectElement = getStructuringElement(cv::MORPH_RECT, Size(13, 4)); morphologyEx(img_open2, img_threshold, CV_MOP_CLOSE, rectElement, cv::Point(-1, -1)); if (config->debugDetector && config->debugShowImages) { imshow("Close", img_threshold); waitKey(0); } //Find contours of possibles plates vector< vector< Point> > contours; findContours(img_threshold, contours, // a vector of contours CV_RETR_EXTERNAL, // retrieve the external contours CV_CHAIN_APPROX_NONE); // all pixels of each contours //Start to iterate to each contour founded vector<vector<Point> >::iterator itc = contours.begin(); vector<RotatedRect> rects; //Remove patch that are no inside limits of aspect ratio and area. while (itc != contours.end()) { //Create bounding rect of object RotatedRect mr = minAreaRect(Mat(*itc)); if (mr.angle < -45.) { mr.angle += 90.0; swap(mr.size.width, mr.size.height); } if (!CheckSizes(mr)) itc = contours.erase(itc); else { ++itc; rects.push_back(mr); } } //Now prunning based on checking all candidate plates for a min/max number of blobsc Mat img_crop, img_crop_b, img_crop_th, img_crop_th_inv; vector< vector< Point> > plateBlobs; vector< vector< Point> > plateBlobsInv; double thresholds[] = { 10, 40, 80, 120, 160, 200, 240 }; const int num_thresholds = 7; int numValidChars = 0; Mat rotated; for (int i = 0; i < rects.size(); i++) { numValidChars = 0; RotatedRect PlateRect = rects[i]; Size rect_size = PlateRect.size; // get the rotation matrix Mat M = getRotationMatrix2D(PlateRect.center, PlateRect.angle, 1.0); // perform the affine transformation warpAffine(frame_gray_cp, rotated, M, frame_gray_cp.size(), INTER_CUBIC); //Crop area around candidate plate getRectSubPix(rotated, rect_size, PlateRect.center, img_crop); if (config->debugDetector && config->debugShowImages) { imshow("Tilt Correction", img_crop); waitKey(0); } for (int z = 0; z < num_thresholds; z++) { cv::threshold(img_crop, img_crop_th, thresholds[z], 255, cv::THRESH_BINARY); cv::threshold(img_crop, img_crop_th_inv, thresholds[z], 255, cv::THRESH_BINARY_INV); findContours(img_crop_th, plateBlobs, // a vector of contours CV_RETR_LIST, // retrieve the contour list CV_CHAIN_APPROX_NONE); // all pixels of each contours findContours(img_crop_th_inv, plateBlobsInv, // a vector of contours CV_RETR_LIST, // retrieve the contour list CV_CHAIN_APPROX_NONE); // all pixels of each contours int numBlobs = plateBlobs.size(); int numBlobsInv = plateBlobsInv.size(); float idealAspect = config->avgCharWidthMM / config->avgCharHeightMM; for (int j = 0; j < numBlobs; j++) { cv::Rect r0 = cv::boundingRect(cv::Mat(plateBlobs[j])); if (ValidateCharAspect(r0, idealAspect)) numValidChars++; } for (int j = 0; j < numBlobsInv; j++) { cv::Rect r0 = cv::boundingRect(cv::Mat(plateBlobsInv[j])); if (ValidateCharAspect(r0, idealAspect)) numValidChars++; } } //If too much or too lcittle might not be a true plate //if (numBlobs < 3 || numBlobs > 50) continue; if (numValidChars < 4 || numValidChars > 50) continue; PlateRegion PlateReg; // Ensure that the rectangle isn't < 0 or > maxWidth/Height Rect bounding_rect = PlateRect.boundingRect(); PlateReg.rect = expandRect(bounding_rect, 0, 0, frame.cols, frame.rows); detectedRegions.push_back(PlateReg); } } return detectedRegions; }
std::vector<OcrChar> TesseractOcr::recognize_line(int line_idx, PipelineData* pipeline_data) { const int SPACE_CHAR_CODE = 32; std::vector<OcrChar> recognized_chars; for (unsigned int i = 0; i < pipeline_data->thresholds.size(); i++) { // Make it black text on white background bitwise_not(pipeline_data->thresholds[i], pipeline_data->thresholds[i]); tesseract.SetImage((uchar*) pipeline_data->thresholds[i].data, pipeline_data->thresholds[i].size().width, pipeline_data->thresholds[i].size().height, pipeline_data->thresholds[i].channels(), pipeline_data->thresholds[i].step1()); int absolute_charpos = 0; for (unsigned int j = 0; j < pipeline_data->charRegions[line_idx].size(); j++) { Rect expandedRegion = expandRect( pipeline_data->charRegions[line_idx][j], 2, 2, pipeline_data->thresholds[i].cols, pipeline_data->thresholds[i].rows) ; tesseract.SetRectangle(expandedRegion.x, expandedRegion.y, expandedRegion.width, expandedRegion.height); tesseract.Recognize(NULL); tesseract::ResultIterator* ri = tesseract.GetIterator(); tesseract::PageIteratorLevel level = tesseract::RIL_SYMBOL; do { const char* symbol = ri->GetUTF8Text(level); float conf = ri->Confidence(level); bool dontcare; int fontindex = 0; int pointsize = 0; const char* fontName = ri->WordFontAttributes(&dontcare, &dontcare, &dontcare, &dontcare, &dontcare, &dontcare, &pointsize, &fontindex); // Ignore NULL pointers, spaces, and characters that are way too small to be valid if(symbol != 0 && symbol[0] != SPACE_CHAR_CODE && pointsize >= config->ocrMinFontSize) { OcrChar c; c.char_index = absolute_charpos; c.confidence = conf; c.letter = string(symbol); recognized_chars.push_back(c); if (this->config->debugOcr) printf("charpos%d line%d: threshold %d: symbol %s, conf: %f font: %s (index %d) size %dpx", absolute_charpos, line_idx, i, symbol, conf, fontName, fontindex, pointsize); bool indent = false; tesseract::ChoiceIterator ci(*ri); do { const char* choice = ci.GetUTF8Text(); OcrChar c2; c2.char_index = absolute_charpos; c2.confidence = ci.Confidence(); c2.letter = string(choice); //1/17/2016 adt adding check to avoid double adding same character if ci is same as symbol. Otherwise first choice from ResultsIterator will get added twice when choiceIterator run. if (string(symbol) != string(choice)) recognized_chars.push_back(c2); else { // Explictly double-adding the first character. This leads to higher accuracy right now, likely because other sections of code // have expected it and compensated. // TODO: Figure out how to remove this double-counting of the first letter without impacting accuracy recognized_chars.push_back(c2); } if (this->config->debugOcr) { if (indent) printf("\t\t "); printf("\t- "); printf("%s conf: %f\n", choice, ci.Confidence()); } indent = true; } while(ci.Next()); } if (this->config->debugOcr) printf("---------------------------------------------\n"); delete[] symbol; } while((ri->Next(level))); delete ri; absolute_charpos++; } } return recognized_chars; }
vector<PlateRegion> Detector::detect(Mat frame, std::vector<cv::Rect> regionsOfInterest) { Mat frame_gray; if (frame.channels() > 2) { cvtColor( frame, frame_gray, CV_BGR2GRAY ); } else { frame.copyTo(frame_gray); } // Apply the detection mask if it has been specified by the user if (detector_mask.mask_loaded) frame_gray = detector_mask.apply_mask(frame_gray); // Setup debug mask image Mat mask_debug_img; if (detector_mask.mask_loaded && config->debugDetector) { frame_gray.copyTo(mask_debug_img); cvtColor(frame_gray, mask_debug_img, CV_GRAY2BGR); } vector<PlateRegion> detectedRegions; for (int i = 0; i < regionsOfInterest.size(); i++) { Rect roi = regionsOfInterest[i]; // Adjust the ROI to be inside the detection mask (if it exists) if (detector_mask.mask_loaded) roi = detector_mask.getRoiInsideMask(roi); // Draw ROIs on debug mask image if (detector_mask.mask_loaded && config->debugDetector) rectangle(mask_debug_img, roi, Scalar(0,255,255), 3); // Sanity check. If roi width or height is less than minimum possible plate size, // then skip it if ((roi.width < config->minPlateSizeWidthPx) || (roi.height < config->minPlateSizeHeightPx)) continue; Mat cropped = frame_gray(roi); int w = cropped.size().width; int h = cropped.size().height; int offset_x = roi.x; int offset_y = roi.y; float scale_factor = computeScaleFactor(w, h); if (scale_factor != 1.0) resize(cropped, cropped, Size(w * scale_factor, h * scale_factor)); float maxWidth = ((float) w) * (config->maxPlateWidthPercent / 100.0f) * scale_factor; float maxHeight = ((float) h) * (config->maxPlateHeightPercent / 100.0f) * scale_factor; Size minPlateSize(config->minPlateSizeWidthPx, config->minPlateSizeHeightPx); Size maxPlateSize(maxWidth, maxHeight); vector<Rect> allRegions = find_plates(cropped, minPlateSize, maxPlateSize); // Aggregate the Rect regions into a hierarchical representation for( unsigned int i = 0; i < allRegions.size(); i++ ) { allRegions[i].x = (allRegions[i].x / scale_factor); allRegions[i].y = (allRegions[i].y / scale_factor); allRegions[i].width = allRegions[i].width / scale_factor; allRegions[i].height = allRegions[i].height / scale_factor; // Ensure that the rectangle isn't < 0 or > maxWidth/Height allRegions[i] = expandRect(allRegions[i], 0, 0, w, h); allRegions[i].x = allRegions[i].x + offset_x; allRegions[i].y = allRegions[i].y + offset_y; } // Check the rectangles and make sure that they're definitely not masked vector<Rect> regions_not_masked; for (unsigned int i = 0; i < allRegions.size(); i++) { if (detector_mask.mask_loaded) { if (!detector_mask.region_is_masked(allRegions[i])) regions_not_masked.push_back(allRegions[i]); } else regions_not_masked.push_back(allRegions[i]); } vector<PlateRegion> orderedRegions = aggregateRegions(regions_not_masked); for (unsigned int j = 0; j < orderedRegions.size(); j++) detectedRegions.push_back(orderedRegions[j]); } // Show debug mask image if (detector_mask.mask_loaded && config->debugDetector && config->debugShowImages) { imshow("Detection Mask", mask_debug_img); } return detectedRegions; }
void MainWindow::documentExport(void) { Canvas *currCanvas = canvas(); QString currFilename = currCanvas->filename(); currFilename.chop(4); if (currFilename.isEmpty()) { // Determine the working directory. QSettings settings; QString workingDir(QDir::homePath()); QVariant workingDirSetting = settings.value("workingDirectory"); if (workingDirSetting.isValid()) { workingDir = workingDirSetting.toString(); } currFilename = workingDir + "/untitled"; } QString filename = QFileDialog::getSaveFileName(this, tr("Export Diagram"), currFilename, tr("SVG (*.svg);;PDF (*.pdf);;Postscript (*.ps)")); if (filename.isEmpty()) { return; } QRectF pageRect = currCanvas->pageRect(); if (pageRect.size().isEmpty()) { // There is no page set, so use a page equal to diagram bounds double padding = 10; pageRect = expandRect(diagramBoundingRect(currCanvas->items()), padding); } QFileInfo file(filename); if (file.suffix() == "svg") { QSvgGenerator generator; generator.setFileName(filename); QRectF targetRect(QPointF(0, 0), currCanvas->sceneRect().size()); QPointF topLeft = pageRect.topLeft() - currCanvas->sceneRect().topLeft(); QRectF viewbox(topLeft, pageRect.size()); generator.setSize(viewbox.size().toSize()); generator.setViewBox(viewbox); generator.setTitle(QFileInfo(filename).fileName()); generator.setDescription(tr("This file was exported from Dunnart. " "http://www.dunnart.org/")); QPainter painter; if (painter.begin(&generator)) { painter.setRenderHint(QPainter::Antialiasing); currCanvas->setRenderingForPrinting(true); currCanvas->render(&painter, targetRect, currCanvas->sceneRect(), Qt::IgnoreAspectRatio); currCanvas->setRenderingForPrinting(false); painter.end(); } else { qDebug("Export SVG painter failed to begin."); } } else { // Use QPrinter for PDF and PS. QPrinter printer; printer.setOutputFileName(filename); printer.setPaperSize(pageRect.size(), QPrinter::Millimeter); QPainter painter; if (painter.begin(&printer)) { painter.setRenderHint(QPainter::Antialiasing); currCanvas->setRenderingForPrinting(true); currCanvas->render(&painter, QRectF(), expandRect(pageRect, -3), Qt::IgnoreAspectRatio); currCanvas->setRenderingForPrinting(false); } else { qDebug("Export PDF/PS painter failed to begin."); } } }