/* * Copyright (c) 2015 OpenALPR Technology, Inc. * Open source Automated License Plate Recognition [http://www.openalpr.com] * * This file is part of OpenALPR. * * OpenALPR is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3 as published by the Free Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "tclap/CmdLine.h" #include "support/filesystem.h" #include "support/timing.h" #include "support/platform.h" #include "video/videobuffer.h" #include "motiondetector.h" #include "alpr.h" using namespace alpr; const std::string MAIN_WINDOW_NAME = "ALPR main window"; const bool SAVE_LAST_VIDEO_STILL = false; const std::string LAST_VIDEO_STILL_LOCATION = "/tmp/laststill.jpg"; const std::string WEBCAM_PREFIX = "/dev/video"; MotionDetector motiondetector; bool do_motiondetection = true; /** Function Headers */ bool detectandshow(Alpr* alpr, cv::Mat frame, std::string region, bool writeJson); bool is_supported_image(std::string image_file); bool measureProcessingTime = false; std::string templatePattern; // This boolean is set to false when the user hits terminates (e.g., CTRL+C ) // so we can end infinite loops for things like video processing. bool program_active = true; int main( int argc, const char** argv ) { std::vector filenames; std::string configFile = ""; bool outputJson = false; int seektoms = 0; bool detectRegion = false; std::string country; int topn; bool debug_mode = false; TCLAP::CmdLine cmd("OpenAlpr Command Line Utility", ' ', Alpr::getVersion()); TCLAP::UnlabeledMultiArg fileArg( "image_file", "Image containing license plates", true, "", "image_file_path" ); TCLAP::ValueArg countryCodeArg("c","country","Country code to identify (either us for USA or eu for Europe). Default=us",false, "us" ,"country_code"); TCLAP::ValueArg seekToMsArg("","seek","Seek to the specified millisecond in a video file. Default=0",false, 0 ,"integer_ms"); TCLAP::ValueArg configFileArg("","config","Path to the openalpr.conf file",false, "" ,"config_file"); TCLAP::ValueArg templatePatternArg("p","pattern","Attempt to match the plate number against a plate pattern (e.g., md for Maryland, ca for California)",false, "" ,"pattern code"); TCLAP::ValueArg topNArg("n","topn","Max number of possible plate numbers to return. Default=10",false, 10 ,"topN"); TCLAP::SwitchArg jsonSwitch("j","json","Output recognition results in JSON format. Default=off", cmd, false); TCLAP::SwitchArg debugSwitch("","debug","Enable debug output. Default=off", cmd, false); TCLAP::SwitchArg detectRegionSwitch("d","detect_region","Attempt to detect the region of the plate image. [Experimental] Default=off", cmd, false); TCLAP::SwitchArg clockSwitch("","clock","Measure/print the total time to process image and all plates. Default=off", cmd, false); TCLAP::SwitchArg motiondetect("", "motion", "Use motion detection on video file or stream. Default=off", cmd, false); try { cmd.add( templatePatternArg ); cmd.add( seekToMsArg ); cmd.add( topNArg ); cmd.add( configFileArg ); cmd.add( fileArg ); cmd.add( countryCodeArg ); if (cmd.parse( argc, argv ) == false) { // Error occurred while parsing. Exit now. return 1; } filenames = fileArg.getValue(); country = countryCodeArg.getValue(); seektoms = seekToMsArg.getValue(); outputJson = jsonSwitch.getValue(); debug_mode = debugSwitch.getValue(); configFile = configFileArg.getValue(); detectRegion = detectRegionSwitch.getValue(); templatePattern = templatePatternArg.getValue(); topn = topNArg.getValue(); measureProcessingTime = clockSwitch.getValue(); do_motiondetection = motiondetect.getValue(); } catch (TCLAP::ArgException &e) // catch any exceptions { std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl; return 1; } cv::Mat frame; Alpr alpr(country, configFile); alpr.setTopN(topn); if (debug_mode) { alpr.getConfig()->setDebug(true); } if (detectRegion) alpr.setDetectRegion(detectRegion); if (templatePattern.empty() == false) alpr.setDefaultRegion(templatePattern); if (alpr.isLoaded() == false) { std::cerr << "Error loading OpenALPR" << std::endl; return 1; } for (unsigned int i = 0; i < filenames.size(); i++) { std::string filename = filenames[i]; if (filename == "-") { std::vector data; int c; while ((c = fgetc(stdin)) != EOF) { data.push_back((uchar) c); } frame = cv::imdecode(cv::Mat(data), 1); if (!frame.empty()) { detectandshow(&alpr, frame, "", outputJson); } else { std::cerr << "Image invalid: " << filename << std::endl; } } else if (filename == "stdin") { std::string filename; while (std::getline(std::cin, filename)) { if (fileExists(filename.c_str())) { frame = cv::imread(filename); detectandshow(&alpr, frame, "", outputJson); } else { std::cerr << "Image file not found: " << filename << std::endl; } } } else if (filename == "webcam" || startsWith(filename, WEBCAM_PREFIX)) { int webcamnumber = 0; // If they supplied "/dev/video[number]" parse the "number" here if(startsWith(filename, WEBCAM_PREFIX) && filename.length() > WEBCAM_PREFIX.length()) { webcamnumber = atoi(filename.substr(WEBCAM_PREFIX.length()).c_str()); } int framenum = 0; cv::VideoCapture cap(webcamnumber); if (!cap.isOpened()) { std::cerr << "Error opening webcam" << std::endl; return 1; } while (cap.read(frame)) { if (framenum == 0) motiondetector.ResetMotionDetection(&frame); detectandshow(&alpr, frame, "", outputJson); sleep_ms(10); framenum++; } } else if (startsWith(filename, "http://") || startsWith(filename, "https://")) { int framenum = 0; VideoBuffer videoBuffer; videoBuffer.connect(filename, 5); cv::Mat latestFrame; while (program_active) { std::vector regionsOfInterest; int response = videoBuffer.getLatestFrame(&latestFrame, regionsOfInterest); if (response != -1) { if (framenum == 0) motiondetector.ResetMotionDetection(&latestFrame); detectandshow(&alpr, latestFrame, "", outputJson); } // Sleep 10ms sleep_ms(10); framenum++; } videoBuffer.disconnect(); std::cout << "Video processing ended" << std::endl; } else if (hasEndingInsensitive(filename, ".avi") || hasEndingInsensitive(filename, ".mp4") || hasEndingInsensitive(filename, ".webm") || hasEndingInsensitive(filename, ".flv") || hasEndingInsensitive(filename, ".mjpg") || hasEndingInsensitive(filename, ".mjpeg") || hasEndingInsensitive(filename, ".mkv") ) { if (fileExists(filename.c_str())) { int framenum = 0; cv::VideoCapture cap = cv::VideoCapture(); cap.open(filename); cap.set(CV_CAP_PROP_POS_MSEC, seektoms); while (cap.read(frame)) { if (SAVE_LAST_VIDEO_STILL) { cv::imwrite(LAST_VIDEO_STILL_LOCATION, frame); } if (!outputJson) std::cout << "Frame: " << framenum << std::endl; if (framenum == 0) motiondetector.ResetMotionDetection(&frame); detectandshow(&alpr, frame, "", outputJson); //create a 1ms delay sleep_ms(1); framenum++; } } else { std::cerr << "Video file not found: " << filename << std::endl; } } else if (is_supported_image(filename)) { if (fileExists(filename.c_str())) { frame = cv::imread(filename); bool plate_found = detectandshow(&alpr, frame, "", outputJson); if (!plate_found && !outputJson) std::cout << "No license plates found." << std::endl; } else { std::cerr << "Image file not found: " << filename << std::endl; } } else if (DirectoryExists(filename.c_str())) { std::vector files = getFilesInDir(filename.c_str()); std::sort(files.begin(), files.end(), stringCompare); for (int i = 0; i < files.size(); i++) { if (is_supported_image(files[i])) { std::string fullpath = filename + "/" + files[i]; std::cout << fullpath << std::endl; frame = cv::imread(fullpath.c_str()); if (detectandshow(&alpr, frame, "", outputJson)) { //while ((char) cv::waitKey(50) != 'c') { } } else { //cv::waitKey(50); } } } } else { std::cerr << "Unknown file type" << std::endl; return 1; } } return 0; } bool is_supported_image(std::string image_file) { return (hasEndingInsensitive(image_file, ".png") || hasEndingInsensitive(image_file, ".jpg") || hasEndingInsensitive(image_file, ".tif") || hasEndingInsensitive(image_file, ".bmp") || hasEndingInsensitive(image_file, ".jpeg") || hasEndingInsensitive(image_file, ".gif")); } bool detectandshow( Alpr* alpr, cv::Mat frame, std::string region, bool writeJson) { timespec startTime; getTimeMonotonic(&startTime); std::vector regionsOfInterest; if (do_motiondetection) { cv::Rect rectan = motiondetector.MotionDetect(&frame); if (rectan.width>0) regionsOfInterest.push_back(AlprRegionOfInterest(rectan.x, rectan.y, rectan.width, rectan.height)); } else regionsOfInterest.push_back(AlprRegionOfInterest(0, 0, frame.cols, frame.rows)); AlprResults results; if (regionsOfInterest.size()>0) results = alpr->recognize(frame.data, frame.elemSize(), frame.cols, frame.rows, regionsOfInterest); timespec endTime; getTimeMonotonic(&endTime); double totalProcessingTime = diffclock(startTime, endTime); if (measureProcessingTime) std::cout << "Total Time to process image: " << totalProcessingTime << "ms." << std::endl; if (writeJson) { std::cout << alpr->toJson( results ) << std::endl; } else { for (int i = 0; i < results.plates.size(); i++) { std::cout << "plate" << i << ": " << results.plates[i].topNPlates.size() << " results"; if (measureProcessingTime) std::cout << " -- Processing Time = " << results.plates[i].processing_time_ms << "ms."; std::cout << std::endl; if (results.plates[i].regionConfidence > 0) std::cout << "State ID: " << results.plates[i].region << " (" << results.plates[i].regionConfidence << "% confidence)" << std::endl; for (int k = 0; k < results.plates[i].topNPlates.size(); k++) { // Replace the multiline newline character with a dash std::string no_newline = results.plates[i].topNPlates[k].characters; std::replace(no_newline.begin(), no_newline.end(), '\n','-'); std::cout << " - " << no_newline << "\t confidence: " << results.plates[i].topNPlates[k].overall_confidence; if (templatePattern.size() > 0 || results.plates[i].regionConfidence > 0) std::cout << "\t pattern_match: " << results.plates[i].topNPlates[k].matches_template; std::cout << std::endl; } } } return results.plates.size() > 0; }