From e5aa1389e0166ea77a1e06dca411668cf574b861 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Fri, 15 May 2015 06:54:02 -0400 Subject: [PATCH] Added camera calibration utility --- src/misc_utilities/CMakeLists.txt | 13 + src/misc_utilities/calibrate.cpp | 394 ++++++++++++++++++++++++++++++ 2 files changed, 407 insertions(+) create mode 100644 src/misc_utilities/calibrate.cpp diff --git a/src/misc_utilities/CMakeLists.txt b/src/misc_utilities/CMakeLists.txt index f46cb44..73767af 100644 --- a/src/misc_utilities/CMakeLists.txt +++ b/src/misc_utilities/CMakeLists.txt @@ -49,9 +49,22 @@ TARGET_LINK_LIBRARIES(openalpr-utils-tagplates ${OpenCV_LIBS} ) +ADD_EXECUTABLE( openalpr-utils-calibrate calibrate.cpp ) +TARGET_LINK_LIBRARIES(openalpr-utils-calibrate + openalpr + support + ${OpenCV_LIBS} + ${Tesseract_LIBRARIES} + ) + + +install (TARGETS openalpr-utils-calibrate DESTINATION bin) + + install (TARGETS openalpr-utils-sortstate DESTINATION bin) install (TARGETS openalpr-utils-classifychars DESTINATION bin) install (TARGETS openalpr-utils-benchmark DESTINATION bin) install (TARGETS openalpr-utils-prepcharsfortraining DESTINATION bin) install (TARGETS openalpr-utils-tagplates DESTINATION bin) +install (TARGETS openalpr-utils-calibrate DESTINATION bin) diff --git a/src/misc_utilities/calibrate.cpp b/src/misc_utilities/calibrate.cpp new file mode 100644 index 0000000..706cd70 --- /dev/null +++ b/src/misc_utilities/calibrate.cpp @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2015 New Designs Unlimited, LLC + * Opensource 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 + +#include "support/filesystem.h" +#include "../tclap/CmdLine.h" +#include "prewarp.h" + +#include + +using namespace std; +using namespace cv; + +const int INSTRUCTIONS_HEIGHT = 32; + + +bool panning; +bool left_clicking; +Point left_click_start; +Point left_click_cur; + + +Point lastPos; + +float w; +float h; +float panX = 0; +float panY = 0; +float rotationx = 0; +float rotationy = 0; +float rotationz = 0; +float stretchX = 1.0; +float dist = 1.0; + +Mat imgOriginal; +Mat curWarpedImage; + +alpr::Config config("us"); + +const string WINDOW_NAME = "Adjust OpenALPR Perspective"; + +string get_config() +{ + stringstream output; + output << "planar," << std::fixed; + output << w << "," << h << ","; + output << rotationx << "," << rotationy << "," << rotationz << ","; + output << stretchX << "," << dist << ","; + output << panX << "," << panY; + + return output.str(); +} + +void drawImage(Mat img) +{ + + config.prewarp = get_config(); + alpr::PreWarp prewarp(&config); + + + if (!left_clicking) + { + curWarpedImage = prewarp.warpImage(img); + } + + + Mat imgWithInstructions(curWarpedImage.rows + INSTRUCTIONS_HEIGHT, curWarpedImage.cols, curWarpedImage.type()); + + curWarpedImage.copyTo(imgWithInstructions(Rect(0, INSTRUCTIONS_HEIGHT, curWarpedImage.cols, curWarpedImage.rows))); + + if (left_clicking) + { + Point start = left_click_start; + Point end = left_click_cur; + start.y += INSTRUCTIONS_HEIGHT; + end.y += INSTRUCTIONS_HEIGHT; + rectangle(imgWithInstructions, start, end, Scalar(255,0,255), 2); + } + + rectangle(imgWithInstructions, Point(0,0), Point(curWarpedImage.cols, INSTRUCTIONS_HEIGHT), Scalar(255,255,255), -1); + + putText(imgWithInstructions, "Press 'o' to output config to console.", + Point(5,25), FONT_HERSHEY_DUPLEX, 1.0, Scalar(0,0,0)); + + + imshow(WINDOW_NAME, imgWithInstructions); + +} + + + + +void mouse_callback(int event, int x, int y, int flags, void* userdata) +{ + y = y - INSTRUCTIONS_HEIGHT; + if (y < 0) + return; + + + if (event == EVENT_RBUTTONDOWN) + { + lastPos.x = x; + lastPos.y = y; + panning = true; + } + if (event == EVENT_RBUTTONUP) + { + panning = false; + drawImage(imgOriginal); + } + if (event == EVENT_MOUSEMOVE && panning) + { + int xdiff = x - lastPos.x; + int ydiff = y - lastPos.y; + panX -= xdiff; + panY -= ydiff; + + lastPos.x = x; + lastPos.y = y; + + // Reduce the computation by only doing it every 3rd pixel + if (x % 3 == 0 && y % 3 == 0) + { + drawImage(imgOriginal); + } + } + + if (event == EVENT_LBUTTONDOWN) + { + left_click_start.x = x; + left_click_start.y = y; + left_clicking = true; + } + if (event == EVENT_LBUTTONUP) + { + left_clicking = false; + drawImage(imgOriginal); + } + if (event == EVENT_MOUSEMOVE && left_clicking) + { + left_click_cur.x = x; + + float IDEAL_PLATE_RATIO = config.charWidthMM / config.charHeightMM; + float curWidth = left_click_cur.x - left_click_start.x; + + left_click_cur.y = left_click_start.y + (IDEAL_PLATE_RATIO * curWidth); + + // Reduce the computation by only doing it every 3rd pixel + if (x % 2 == 0 || y % 2 == 0) + { + drawImage(imgOriginal); + } + } + +} + +void ZChange(int pos, void* userdata) +{ + + rotationz = -((float)pos - 100) / 100.0; + + drawImage(imgOriginal); +} + +void XChange(int pos, void* userdata) +{ + + rotationx = -((float)pos - 100) / 20000.0; + + drawImage(imgOriginal); +} + + +void YChange(int pos, void* userdata) +{ + rotationy = ((float)pos - 100) / 20000.0; + + drawImage(imgOriginal); +} + + +void DistChange(int pos, void* userdata) +{ + dist = 1.0 - ((float)pos - 100) / 200.0; + + drawImage(imgOriginal); +} + +void StretchChange(int pos, void* userdata) +{ + stretchX = 1.0 + ((float)pos - 100) / -200.0; + + drawImage(imgOriginal); +} + + +int value; +void create_window() +{ + namedWindow(WINDOW_NAME, CV_WINDOW_AUTOSIZE | CV_WINDOW_KEEPRATIO | CV_GUI_EXPANDED); + + + value = 100; + panX = 0; + panY = 0; + + XChange(100, NULL); + YChange(100, NULL); + ZChange(100, NULL); + DistChange(100, NULL); + + + createTrackbar( "X", WINDOW_NAME, &value, 200, XChange); + createTrackbar( "Y", WINDOW_NAME, &value, 200, YChange); + createTrackbar( "Z", WINDOW_NAME, &value, 200, ZChange); + createTrackbar( "W", WINDOW_NAME, &value, 200, StretchChange); + createTrackbar( "D", WINDOW_NAME, &value, 200, DistChange); + + + setMouseCallback(WINDOW_NAME, mouse_callback, NULL); + + +} + +/* + * + */ +int main(int argc, char** argv) { + + + string filename; + string country; + string config_path; + string translate_config; + int max_width; + int max_height; + + TCLAP::CmdLine cmd("OpenAlpr Perspective Utility", ' ', "0.1"); + + TCLAP::UnlabeledValueArg 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 configFileArg("","config","Path to the openalpr.conf file",false, "" ,"config_file"); + + TCLAP::ValueArg translateTestArg("t","test","Test an image using the provided translation config",false, "" ,"prewarp config"); + + TCLAP::ValueArg maxWidthArg("w", "maxwidth", "Max Width used for displaying image in this utility. Default=1280",false, 1280 ,"max width"); + TCLAP::ValueArg maxHeightArg("", "maxheight", "Max Height used for displaying image in this utility. Default=1024",false, 1024 ,"max height"); + + try + { + cmd.add( configFileArg ); + cmd.add( fileArg ); + cmd.add( countryCodeArg ); + cmd.add( translateTestArg ); + cmd.add( maxWidthArg ); + cmd.add( maxHeightArg ); + + + if (cmd.parse( argc, argv ) == false) + { + // Error occured while parsing. Exit now. + return 1; + } + + filename = fileArg.getValue(); + country = countryCodeArg.getValue(); + config_path = configFileArg.getValue(); + translate_config = translateTestArg.getValue(); + max_width = maxWidthArg.getValue(); + max_height = maxHeightArg.getValue(); + + } + catch (TCLAP::ArgException &e) // catch any exceptions + { + std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl; + return 1; + } + + if (!alpr::fileExists(filename.c_str())) + { + cerr << "Could not find image file: " << filename << endl; + } + + config = alpr::Config(country); + + panning = false; + left_clicking = false; + + + imgOriginal = imread(filename); + + if (imgOriginal.cols > max_width) + { + float aspect = max_width / ((float)imgOriginal.cols); + float y = ((float)imgOriginal.rows) * aspect; + + resize(imgOriginal, imgOriginal, Size((int) max_width, (int) y)); + } + if (imgOriginal.rows > max_height) + { + float aspect = max_height / ((float)imgOriginal.rows); + float x = ((float)imgOriginal.cols) * aspect; + + resize(imgOriginal, imgOriginal, Size((int) x, (int) max_height)); + } + + w = imgOriginal.cols; + h = imgOriginal.rows; + + create_window(); + + + if (translate_config != "") + { + int first_comma = translate_config.find(","); + + + string name = translate_config.substr(0, first_comma); + stringstream ss(translate_config.substr(first_comma + 1, translate_config.length())); + + ss >> w; + ss.ignore(); + ss >> h; + ss.ignore(); + ss >> rotationx; + ss.ignore(); // Ignore comma + ss >> rotationy; + ss.ignore(); // Ignore comma + ss >> rotationz; + ss.ignore(); // Ignore comma + ss >> stretchX; + ss.ignore(); // Ignore comma + ss >> dist; + ss.ignore(); // Ignore comma + ss >> panX; + ss.ignore(); // Ignore comma + ss >> panY; + + } + + float width_ratio = w / ((float)imgOriginal.cols); + float height_ratio = h / ((float)imgOriginal.rows); + w = imgOriginal.cols; + h = imgOriginal.rows; + rotationx *=width_ratio; + rotationy *=width_ratio; + panX /= width_ratio; + panY /= height_ratio; + + drawImage(imgOriginal); + + + while (true) + { + + char c = waitKey(15); + + if (c == 'o') + { + cout << "prewarp = " << get_config() << endl; + + } + } + + cvDestroyAllWindows(); + + + return 0; +} +