diff --git a/distros/debian/control b/distros/debian/control index 78b8301..90a676b 100644 --- a/distros/debian/control +++ b/distros/debian/control @@ -69,3 +69,13 @@ Description: Utilities for the OpenALPR library OpenALPR is an open source Automatic License Plate Recognition library written in C++. The library analyzes images and identifies license plates. The output is the text representation of any license plate characters found in the image. + +Package: python-openalpr +Section: python +Architecture: amd64 +Depends: libopenalpr-dev (= ${binary:Version}), ${misc:Depends} +Description: Python binding for OpenALPR library + OpenALPR is an open source Automatic License Plate Recognition library written + in C++. The library analyzes images and identifies license plates. The output + is the text representation of any license plate characters found in the image. + diff --git a/distros/debian/python-openalpr.install b/distros/debian/python-openalpr.install new file mode 100644 index 0000000..4d43ce6 --- /dev/null +++ b/distros/debian/python-openalpr.install @@ -0,0 +1 @@ +usr/lib/python2.7/dist-packages/openalpr \ No newline at end of file diff --git a/runtime_data/config/au.conf b/runtime_data/config/au.conf index 19d2bd4..d3fd4a9 100644 --- a/runtime_data/config/au.conf +++ b/runtime_data/config/au.conf @@ -30,3 +30,7 @@ min_plate_size_width_px = 85 min_plate_size_height_px = 28 ocr_language = lau + +; Override for postprocess letters/numbers regex. +postprocess_regex_letters = [A-Z] +postprocess_regex_numbers = [0-9] \ No newline at end of file diff --git a/runtime_data/config/auwide.conf b/runtime_data/config/auwide.conf index 9fdbe79..490e7b3 100644 --- a/runtime_data/config/auwide.conf +++ b/runtime_data/config/auwide.conf @@ -31,3 +31,7 @@ min_plate_size_width_px = 100 min_plate_size_height_px = 20 ocr_language = lau + +; Override for postprocess letters/numbers regex. +postprocess_regex_letters = [A-Z] +postprocess_regex_numbers = [0-9] \ No newline at end of file diff --git a/runtime_data/config/eu.conf b/runtime_data/config/eu.conf index 8d00aa5..b3d3f69 100644 --- a/runtime_data/config/eu.conf +++ b/runtime_data/config/eu.conf @@ -32,3 +32,7 @@ min_plate_size_width_px = 65 min_plate_size_height_px = 18 ocr_language = leu + +; Override for postprocess letters/numbers regex. +postprocess_regex_letters = [A-Z] +postprocess_regex_numbers = [0-9] \ No newline at end of file diff --git a/runtime_data/config/kr.conf b/runtime_data/config/kr.conf index cd4b846..e0ca45f 100644 --- a/runtime_data/config/kr.conf +++ b/runtime_data/config/kr.conf @@ -5,7 +5,7 @@ char_analysis_height_range = 0.15 char_analysis_height_step_size = 0.10 char_analysis_height_num_steps = 5 -segmentation_min_box_width_px = 5 +segmentation_min_box_width_px = 4 segmentation_min_charheight_percent = 0.4; segmentation_max_segment_width_percent_vs_average = 2.0; @@ -15,7 +15,7 @@ plate_height_mm = 110 multiline = 0 char_height_mm = 80 -char_width_mm = 53 +char_width_mm = 43 char_whitespace_top_mm = 10 char_whitespace_bot_mm = 10 @@ -31,3 +31,7 @@ min_plate_size_width_px = 100 min_plate_size_height_px = 20 ocr_language = lkr + +; Override for postprocess letters/numbers regex. +postprocess_regex_letters = \pL +postprocess_regex_numbers = \pN diff --git a/runtime_data/config/us.conf b/runtime_data/config/us.conf index d311841..c9f8124 100644 --- a/runtime_data/config/us.conf +++ b/runtime_data/config/us.conf @@ -30,3 +30,7 @@ min_plate_size_width_px = 70 min_plate_size_height_px = 35 ocr_language = lus + +; Override for postprocess letters/numbers regex. +postprocess_regex_letters = [A-Z] +postprocess_regex_numbers = [0-9] \ No newline at end of file diff --git a/runtime_data/postprocess/eu.patterns b/runtime_data/postprocess/eu.patterns index 3e30861..b528f21 100644 --- a/runtime_data/postprocess/eu.patterns +++ b/runtime_data/postprocess/eu.patterns @@ -36,22 +36,22 @@ gi [G]####@ gr @@@#### hu @@@### is @@@## -ie [12]##@###### -ie [12]##@@###### -ie [12]##@##### -ie [12]##@@##### -ie [12]##@#### -ie [12]##@@#### -ie [12]##@### -ie [12]##@@### -ie ##@###### -ie ##@@###### -ie ##@##### -ie ##@@##### -ie ##@#### -ie ##@@#### -ie ##@### -ie ##@@### +ie ##[12][CDGLTW]###### +ie ##[12][CDGKLMORSTW][DEHKLMNOSWXY]###### +ie ##[12][CDGLTW]##### +ie ##[12][CDGKLMORSTW][DEHKLMNOSWXY]##### +ie ##[12][CDGLTW]#### +ie ##[12][CDGKLMORSTW][DEHKLMNOSWXY]#### +ie ##[12][CDGLTW]### +ie ##[12][CDGKLMORSTW][DEHKLMNOSWXY]### +ie ##[CDGLTW]###### +ie ##[CDGKLMORSTW][DEHKLMNOSWXY]###### +ie ##[CDGLTW]##### +ie ##[CDGKLMORSTW][DEHKLMNOSWXY]##### +ie ##[CDGLTW]#### +ie ##[CDGKLMORSTW][DEHKLMNOSWXY]#### +ie ##[CDGLTW]### +ie ##[CDGKLMORSTW][DEHKLMNOSWXY]### it @@###@@ kz ###@@@ lv @@#### diff --git a/runtime_data/postprocess/us.patterns b/runtime_data/postprocess/us.patterns index ea5df8c..45f1caf 100644 --- a/runtime_data/postprocess/us.patterns +++ b/runtime_data/postprocess/us.patterns @@ -203,6 +203,8 @@ va ####@@ va #####[JY] wa ###@@@ wa @@@#### +wi ###@@@ +wi @@@### wv [1-9DON]@@### wv [1-9DON]@#### wy ###### diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e898647..2a89c54 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -101,6 +101,7 @@ ENDIF() TARGET_LINK_LIBRARIES(alpr ${OPENALPR_LIB} + statedetection support video ${OpenCV_LIBS} @@ -134,6 +135,7 @@ add_subdirectory(tests) ENDIF() add_subdirectory(openalpr) +add_subdirectory(statedetection) add_subdirectory(video) if (WITH_BINDING_JAVA) diff --git a/src/bindings/csharp/openalpr-net/AssemblyInfo.cpp b/src/bindings/csharp/openalpr-net/AssemblyInfo.cpp index ac5af8d..cc20aa3 100644 --- a/src/bindings/csharp/openalpr-net/AssemblyInfo.cpp +++ b/src/bindings/csharp/openalpr-net/AssemblyInfo.cpp @@ -38,5 +38,3 @@ using namespace System::Security::Permissions; [assembly:ComVisible(false)]; [assembly:CLSCompliantAttribute(true)]; - -[assembly:SecurityPermission(SecurityAction::RequestMinimum, UnmanagedCode = true)]; diff --git a/src/bindings/csharp/openalpr-net/bitmapmat-net.h b/src/bindings/csharp/openalpr-net/bitmapmat-net.h index a65acf3..cedbf94 100644 --- a/src/bindings/csharp/openalpr-net/bitmapmat-net.h +++ b/src/bindings/csharp/openalpr-net/bitmapmat-net.h @@ -65,7 +65,7 @@ namespace openalprnet { channels = 4; break; default: - throw gcnew NotImplementedException(); + throw gcnew NotSupportedException(bitmap->PixelFormat.ToString()); } BitmapData^ bitmapData = bitmap->LockBits( diff --git a/src/bindings/csharp/openalpr-net/helper-net.h b/src/bindings/csharp/openalpr-net/helper-net.h index ce93a1b..806ace0 100644 --- a/src/bindings/csharp/openalpr-net/helper-net.h +++ b/src/bindings/csharp/openalpr-net/helper-net.h @@ -50,6 +50,11 @@ namespace openalprnet static Bitmap^ MatToBitmap(cv::Mat mat) { + if (mat.empty()) + { + return nullptr; + } + const int width = mat.size().width; const int height = mat.size().height; const int channels = mat.channels(); diff --git a/src/bindings/csharp/openalpr-net/openalpr-net.cpp b/src/bindings/csharp/openalpr-net/openalpr-net.cpp index 4988653..d1519c9 100644 --- a/src/bindings/csharp/openalpr-net/openalpr-net.cpp +++ b/src/bindings/csharp/openalpr-net/openalpr-net.cpp @@ -326,7 +326,7 @@ namespace openalprnet { event EventHandler^ FrameProcessed; - void recognizeFromVideo(System::String^ videoPath) { + void RecognizeFromVideo(System::String^ videoPath) { if (System::IO::File::Exists(videoPath)) { int framenum = 0; cv::VideoCapture cap = cv::VideoCapture(); @@ -409,8 +409,9 @@ namespace openalprnet { /// AlprResultsNet^ Recognize(MemoryStream^ memoryStream, List^ regionsOfInterest) { - std::vector p = AlprHelper::MemoryStreamToVector(memoryStream); - AlprResults results = m_Impl->recognize(p); + std::vector buffer = AlprHelper::MemoryStreamToVector(memoryStream); + std::vector rois = AlprHelper::ToVector(regionsOfInterest); + AlprResults results = m_Impl->recognize(buffer, rois); return gcnew AlprResultsNet(results); } @@ -427,8 +428,9 @@ namespace openalprnet { /// /// Bytes representing image data AlprResultsNet^ Recognize(cli::array^ imageBuffer, List^ regionsOfInterest) { - std::vector p = AlprHelper::ToVector(imageBuffer); - AlprResults results = m_Impl->recognize(p); + std::vector buffer = AlprHelper::ToVector(imageBuffer); + std::vector rois = AlprHelper::ToVector(regionsOfInterest); + AlprResults results = m_Impl->recognize(buffer, rois); return gcnew AlprResultsNet(results); } @@ -450,6 +452,32 @@ namespace openalprnet { return gcnew AlprResultsNet(results); } + /// + /// Pre-warp from raw pixel data. + /// + array^ PreWarp(array^ imageBuffer) + { + std::vector buffer = AlprHelper::ToVector(imageBuffer); + cv::Mat src = cv::imdecode(buffer, 1); + + alpr::PreWarp *preWarp = new alpr::PreWarp(m_Impl->getConfig()); + cv::Mat warpedImage = preWarp->warpImage(src); + + std::vector warpedImageVector; + cv::imencode(".jpg", warpedImage, warpedImageVector); + + const size_t warpedImageSize = warpedImageVector.size(); + + array^ warpedImageByteArray = gcnew array(warpedImageSize); + pin_ptr pin(&warpedImageByteArray[0]); + + std::memcpy(pin, &warpedImageVector[0], warpedImageSize); + + delete preWarp; + + return warpedImageByteArray; + } + bool IsLoaded() { return m_Impl->isLoaded(); } diff --git a/src/bindings/csharp/openalpr-net/openalpr-net.h b/src/bindings/csharp/openalpr-net/openalpr-net.h index 52ec33e..ca26c5e 100644 --- a/src/bindings/csharp/openalpr-net/openalpr-net.h +++ b/src/bindings/csharp/openalpr-net/openalpr-net.h @@ -1,6 +1,7 @@ #pragma once #include "alpr.h" +#include "prewarp.h" #include #include diff --git a/src/bindings/csharp/openalpr-net/openalpr-net.vcxproj b/src/bindings/csharp/openalpr-net/openalpr-net.vcxproj index 091d751..419977b 100644 --- a/src/bindings/csharp/openalpr-net/openalpr-net.vcxproj +++ b/src/bindings/csharp/openalpr-net/openalpr-net.vcxproj @@ -25,6 +25,7 @@ openalprnet 2.1.0 v120 + v140 ..\..\..\..\windows None $(OpenALPRWindowsDir)\build\dist\$(OpenALPRVersion)\$(PlatformToolset)\$(Configuration)\$(Platform) @@ -105,7 +106,7 @@ true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;$(OpenALPRDistDir)\opencv_videostab$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_ts$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_stitching$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_contrib$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\libtesseract$(TesseractVersion)-static$(TesseractDebugPrefix).lib;$(OpenALPRDistDir)\liblept$(LeptonicaVersion)$(DebugPrefix).lib;ws2_32.lib;$(OpenALPRDistDir)\opencv_nonfree$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_gpu$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_photo$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_objdetect$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_legacy$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_video$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_ml$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_calib3d$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_features2d$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_highgui$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_imgproc$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_flann$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_core$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\support.lib;$(OpenALPRDistDir)\openalpr-static.lib + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;$(OpenALPRDistDir)\opencv_videostab$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_ts$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_stitching$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_contrib$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\libtesseract$(TesseractVersion)-static$(TesseractDebugPrefix).lib;$(OpenALPRDistDir)\liblept$(LeptonicaVersion)$(DebugPrefix).lib;ws2_32.lib;$(OpenALPRDistDir)\opencv_nonfree$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_gpu$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_photo$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_objdetect$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_legacy$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_video$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_ml$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_calib3d$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_features2d$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_highgui$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_imgproc$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_flann$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_core$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\support.lib;$(OpenALPRDistDir)\openalpr-static.lib;$(OpenALPRDistDir)\statedetection.lib @@ -118,7 +119,7 @@ true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;$(OpenALPRDistDir)\opencv_videostab$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_ts$(OpenCVVersion)$(DebugPrefix).lib;$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_stitching$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_contrib$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\libtesseract$(TesseractVersion)-static$(TesseractDebugPrefix).lib;$(OpenALPRDistDir)\liblept$(LeptonicaVersion)$(DebugPrefix).lib;ws2_32.lib;$(OpenALPRDistDir)\opencv_nonfree$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_gpu$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_photo$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_objdetect$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_legacy$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_video$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_ml$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_calib3d$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_features2d$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_highgui$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_imgproc$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_flann$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_core$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\support.lib;$(OpenALPRDistDir)\openalpr-static.lib + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;$(OpenALPRDistDir)\opencv_videostab$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_ts$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_stitching$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_contrib$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\libtesseract$(TesseractVersion)-static$(TesseractDebugPrefix).lib;$(OpenALPRDistDir)\liblept$(LeptonicaVersion)$(DebugPrefix).lib;ws2_32.lib;$(OpenALPRDistDir)\opencv_nonfree$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_gpu$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_photo$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_objdetect$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_legacy$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_video$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_ml$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_calib3d$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_features2d$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_highgui$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_imgproc$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_flann$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_core$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\support.lib;$(OpenALPRDistDir)\openalpr-static.lib;$(OpenALPRDistDir)\statedetection.lib @@ -130,7 +131,7 @@ true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;$(OpenALPRDistDir)\opencv_videostab$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_ts$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_stitching$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_contrib$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\libtesseract$(TesseractVersion)-static$(TesseractDebugPrefix).lib;$(OpenALPRDistDir)\liblept$(LeptonicaVersion)$(DebugPrefix).lib;ws2_32.lib;$(OpenALPRDistDir)\opencv_nonfree$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_gpu$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_photo$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_objdetect$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_legacy$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_video$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_ml$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_calib3d$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_features2d$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_highgui$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_imgproc$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_flann$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_core$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\support.lib;$(OpenALPRDistDir)\openalpr-static.lib + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;$(OpenALPRDistDir)\opencv_videostab$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_ts$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_stitching$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_contrib$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\libtesseract$(TesseractVersion)-static$(TesseractDebugPrefix).lib;$(OpenALPRDistDir)\liblept$(LeptonicaVersion)$(DebugPrefix).lib;ws2_32.lib;$(OpenALPRDistDir)\opencv_nonfree$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_gpu$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_photo$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_objdetect$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_legacy$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_video$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_ml$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_calib3d$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_features2d$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_highgui$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_imgproc$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_flann$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_core$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\support.lib;$(OpenALPRDistDir)\openalpr-static.lib;$(OpenALPRDistDir)\statedetection.lib @@ -142,7 +143,7 @@ true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;$(OpenALPRDistDir)\opencv_videostab$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_ts$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_stitching$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_contrib$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\libtesseract$(TesseractVersion)-static$(TesseractDebugPrefix).lib;$(OpenALPRDistDir)\liblept$(LeptonicaVersion)$(DebugPrefix).lib;ws2_32.lib;$(OpenALPRDistDir)\opencv_nonfree$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_gpu$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_photo$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_objdetect$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_legacy$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_video$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_ml$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_calib3d$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_features2d$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_highgui$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_imgproc$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_flann$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_core$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\support.lib;$(OpenALPRDistDir)\openalpr-static.lib + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;$(OpenALPRDistDir)\opencv_videostab$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_ts$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_stitching$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_contrib$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\libtesseract$(TesseractVersion)-static$(TesseractDebugPrefix).lib;$(OpenALPRDistDir)\liblept$(LeptonicaVersion)$(DebugPrefix).lib;ws2_32.lib;$(OpenALPRDistDir)\opencv_nonfree$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_gpu$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_photo$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_objdetect$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_legacy$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_video$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_ml$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_calib3d$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_features2d$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_highgui$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_imgproc$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_flann$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\opencv_core$(OpenCVVersion)$(DebugPrefix).lib;$(OpenALPRDistDir)\support.lib;$(OpenALPRDistDir)\openalpr-static.lib;$(OpenALPRDistDir)\statedetection.lib diff --git a/src/bindings/python/CMakeLists.txt b/src/bindings/python/CMakeLists.txt index 6466d87..91d4fe5 100644 --- a/src/bindings/python/CMakeLists.txt +++ b/src/bindings/python/CMakeLists.txt @@ -17,4 +17,7 @@ set_target_properties(openalprpy PROPERTIES SOVERSION ${OPENALPR_MAJOR_VERSION}) TARGET_LINK_LIBRARIES(openalprpy openalpr) -install (TARGETS openalprpy DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) \ No newline at end of file +install (TARGETS openalprpy DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) +install (DIRECTORY openalpr DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/python2.7/dist-packages + USE_SOURCE_PERMISSIONS +) diff --git a/src/bindings/python/openalpr/__init__.py b/src/bindings/python/openalpr/__init__.py new file mode 100644 index 0000000..2871d69 --- /dev/null +++ b/src/bindings/python/openalpr/__init__.py @@ -0,0 +1 @@ +from openalpr import Alpr diff --git a/src/bindings/python/openalpr.py b/src/bindings/python/openalpr/openalpr.py similarity index 64% rename from src/bindings/python/openalpr.py rename to src/bindings/python/openalpr/openalpr.py index 3cb8883..73801bf 100644 --- a/src/bindings/python/openalpr.py +++ b/src/bindings/python/openalpr/openalpr.py @@ -13,48 +13,52 @@ class Alpr(): self._initialize_func = self._openalprpy_lib.initialize + self._initialize_func.restype = ctypes.c_void_p self._initialize_func.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p] self._dispose_func = self._openalprpy_lib.dispose + self._dispose_func.argtypes = [ctypes.c_void_p] self._is_loaded_func = self._openalprpy_lib.isLoaded + self._is_loaded_func.argtypes = [ctypes.c_void_p] self._is_loaded_func.restype = ctypes.c_bool self._recognize_file_func = self._openalprpy_lib.recognizeFile self._recognize_file_func.restype = ctypes.c_void_p - self._recognize_file_func.argtypes = [ctypes.c_char_p] + self._recognize_file_func.argtypes = [ctypes.c_void_p, ctypes.c_char_p] self._recognize_array_func = self._openalprpy_lib.recognizeArray self._recognize_array_func.restype = ctypes.c_void_p - self._recognize_array_func.argtypes = [ctypes.POINTER(ctypes.c_ubyte), ctypes.c_uint] + self._recognize_array_func.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_ubyte), ctypes.c_uint] self._free_json_mem_func = self._openalprpy_lib.freeJsonMem self._set_default_region_func = self._openalprpy_lib.setDefaultRegion - self._set_default_region_func.argtypes = [ctypes.c_char_p] + self._set_default_region_func.argtypes = [ctypes.c_void_p, ctypes.c_char_p] self._set_detect_region_func = self._openalprpy_lib.setDetectRegion - self._set_detect_region_func.argtypes = [ctypes.c_bool] + self._set_detect_region_func.argtypes = [ctypes.c_void_p, ctypes.c_bool] self._set_top_n_func = self._openalprpy_lib.setTopN - self._set_top_n_func.argtypes = [ctypes.c_int] + self._set_top_n_func.argtypes = [ctypes.c_void_p, ctypes.c_int] self._get_version_func = self._openalprpy_lib.getVersion + self._get_version_func.argtypes = [ctypes.c_void_p] self._get_version_func.restype = ctypes.c_void_p - self._initialize_func(country, config_file, runtime_dir) + self.alpr_pointer = self._initialize_func(country, config_file, runtime_dir) def unload(self): - self._openalprpy_lib.dispose() + self._openalprpy_lib.dispose(self.alpr_pointer) def is_loaded(self): - return self._is_loaded_func() + return self._is_loaded_func(self.alpr_pointer) def recognize_file(self, file_path): - ptr = self._recognize_file_func(file_path) + ptr = self._recognize_file_func(self.alpr_pointer, file_path) json_data = ctypes.cast(ptr, ctypes.c_char_p).value response_obj = json.loads(json_data) self._free_json_mem_func(ctypes.c_void_p(ptr)) @@ -64,7 +68,7 @@ class Alpr(): def recognize_array(self, byte_array): pb = ctypes.cast(byte_array, ctypes.POINTER(ctypes.c_ubyte)) - ptr = self._recognize_array_func(pb, len(byte_array)) + ptr = self._recognize_array_func(self.alpr_pointer, pb, len(byte_array)) json_data = ctypes.cast(ptr, ctypes.c_char_p).value response_obj = json.loads(json_data) self._free_json_mem_func(ctypes.c_void_p(ptr)) @@ -73,19 +77,19 @@ class Alpr(): def get_version(self): - ptr = self._get_version_func() + ptr = self._get_version_func(self.alpr_pointer) version_number = ctypes.cast(ptr, ctypes.c_char_p).value self._free_json_mem_func(ctypes.c_void_p(ptr)) return version_number def set_top_n(self, topn): - self._set_top_n_func(topn) + self._set_top_n_func(self.alpr_pointer, topn) def set_default_region(self, region): - self._set_default_region_func(region) + self._set_default_region_func(self.alpr_pointer, region) def set_detect_region(self, enabled): - self._set_detect_region_func(enabled) + self._set_detect_region_func(self.alpr_pointer, enabled) diff --git a/src/bindings/python/openalprpy.cpp b/src/bindings/python/openalprpy.cpp index a2ca3f6..331df29 100644 --- a/src/bindings/python/openalprpy.cpp +++ b/src/bindings/python/openalprpy.cpp @@ -16,10 +16,8 @@ extern "C" { using namespace alpr; - bool initialized = false; - static Alpr* nativeAlpr; - OPENALPR_EXPORT void initialize(char* ccountry, char* cconfigFile, char* cruntimeDir) + OPENALPR_EXPORT Alpr* initialize(char* ccountry, char* cconfigFile, char* cruntimeDir) { //printf("Initialize"); @@ -29,35 +27,29 @@ extern "C" { std::string runtimeDir(cruntimeDir); //std::cout << country << std::endl << configFile << std::endl << runtimeDir << std::endl; - nativeAlpr = new alpr::Alpr(country, configFile, runtimeDir); + Alpr* nativeAlpr = new alpr::Alpr(country, configFile, runtimeDir); - initialized = true; - return; + return nativeAlpr; } - OPENALPR_EXPORT void dispose() + OPENALPR_EXPORT void dispose(Alpr* nativeAlpr) { - //printf("Dispose"); - initialized = false; delete nativeAlpr; } - OPENALPR_EXPORT bool isLoaded() + OPENALPR_EXPORT bool isLoaded(Alpr* nativeAlpr) { //printf("IS LOADED"); - if (!initialized) - return false; - return nativeAlpr->isLoaded(); } - OPENALPR_EXPORT char* recognizeFile(char* cimageFile) + OPENALPR_EXPORT char* recognizeFile(Alpr* nativeAlpr, char* cimageFile) { //printf("Recognize file"); @@ -83,12 +75,13 @@ extern "C" { } - OPENALPR_EXPORT char* recognizeArray(unsigned char* buf, int len) + OPENALPR_EXPORT char* recognizeArray(Alpr* nativeAlpr, unsigned char* buf, int len) { //printf("Recognize byte array"); //printf("buffer pointer: %p\n", buf); //printf("buffer length: %d\n", len); - + + //std::cout << "Using instance: " << nativeAlpr << std::endl; std::vector cvec(buf, buf+len); @@ -103,7 +96,7 @@ extern "C" { return membuffer; } - OPENALPR_EXPORT void setDefaultRegion(char* cdefault_region) + OPENALPR_EXPORT void setDefaultRegion(Alpr* nativeAlpr, char* cdefault_region) { // Convert strings from java to C++ and release resources std::string default_region(cdefault_region); @@ -111,17 +104,17 @@ extern "C" { nativeAlpr->setDefaultRegion(default_region); } - OPENALPR_EXPORT void setDetectRegion(bool detect_region) + OPENALPR_EXPORT void setDetectRegion(Alpr* nativeAlpr, bool detect_region) { nativeAlpr->setDetectRegion(detect_region); } - OPENALPR_EXPORT void setTopN(int top_n) + OPENALPR_EXPORT void setTopN(Alpr* nativeAlpr, int top_n) { nativeAlpr->setTopN(top_n); } - OPENALPR_EXPORT char* getVersion() + OPENALPR_EXPORT char* getVersion(Alpr* nativeAlpr) { std::string version = nativeAlpr->getVersion(); diff --git a/src/bindings/python/setup.py b/src/bindings/python/setup.py new file mode 100644 index 0000000..5c503d3 --- /dev/null +++ b/src/bindings/python/setup.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +from distutils.core import setup + +setup(name='openalpr', + version='1.0', + description='OpenALPR Python Bindings', + author='Matt Hill', + author_email='matthill@openalpr.com', + url='http://www.openalpr.com/', + packages=['openalpr'] + ) diff --git a/src/bindings/python/test.py b/src/bindings/python/test.py index 5a068f6..e350f07 100644 --- a/src/bindings/python/test.py +++ b/src/bindings/python/test.py @@ -54,4 +54,4 @@ try: finally: if alpr: - alpr.unload() \ No newline at end of file + alpr.unload() diff --git a/src/main.cpp b/src/main.cpp index fd9b429..4106c5c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -198,7 +198,9 @@ int main( int argc, const char** argv ) 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, ".flv") || hasEndingInsensitive(filename, ".mjpg") || hasEndingInsensitive(filename, ".mjpeg") || + hasEndingInsensitive(filename, ".mkv") + ) { if (fileExists(filename.c_str())) { diff --git a/src/misc_utilities/CMakeLists.txt b/src/misc_utilities/CMakeLists.txt index 969cb9d..7232c4d 100644 --- a/src/misc_utilities/CMakeLists.txt +++ b/src/misc_utilities/CMakeLists.txt @@ -2,6 +2,7 @@ ADD_EXECUTABLE( openalpr-utils-sortstate sortstate.cpp ) TARGET_LINK_LIBRARIES(openalpr-utils-sortstate ${OPENALPR_LIB} + statedetection support ${OpenCV_LIBS} ${Tesseract_LIBRARIES} @@ -23,6 +24,7 @@ ADD_EXECUTABLE(openalpr-utils-benchmark ) TARGET_LINK_LIBRARIES(openalpr-utils-benchmark ${OPENALPR_LIB} + statedetection support pthread ${OpenCV_LIBS} @@ -72,4 +74,3 @@ ENDIF() 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/benchmarks/benchmark.cpp b/src/misc_utilities/benchmarks/benchmark.cpp index ecde796..fa71ce2 100644 --- a/src/misc_utilities/benchmarks/benchmark.cpp +++ b/src/misc_utilities/benchmarks/benchmark.cpp @@ -167,7 +167,7 @@ int main( int argc, const char** argv ) alpr.setDetectRegion(true); Detector* plateDetector = createDetector(&config); - StateIdentifier stateIdentifier(&config); + StateDetector stateDetector(country, config.runtimeBaseDir); OCR ocr(&config); vector endToEndTimes; @@ -210,8 +210,8 @@ int main( int argc, const char** argv ) PipelineData pipeline_data(frame, regions[z].rect, &config); getTimeMonotonic(&startTime); - - stateIdentifier.recognize(&pipeline_data); + + //stateDetector.detect(&pipeline_data); getTimeMonotonic(&endTime); double stateidTime = diffclock(startTime, endTime); cout << "\tRegion " << z << ": State ID time: " << stateidTime << "ms." << endl; diff --git a/src/misc_utilities/classifychars.cpp b/src/misc_utilities/classifychars.cpp index ec403a2..f9a3cbd 100644 --- a/src/misc_utilities/classifychars.cpp +++ b/src/misc_utilities/classifychars.cpp @@ -26,7 +26,6 @@ #include "postprocess/regexrule.h" #include "licenseplatecandidate.h" -#include "stateidentifier.h" #include "utility.h" #include "support/filesystem.h" #include "ocr.h" @@ -324,7 +323,7 @@ vector showCharSelection(Mat image, vector charRegions, string sta for (int i = 0; i < charRegions.size(); i++) humanInputs[i] = SPACE; - RegexRule regex_rule("", "[\\p{Digit}\\p{Alpha}]"); + RegexRule regex_rule("", "[\\pL\\pN]", "", ""); int16_t waitkey = waitKey(50); while (waitkey != ENTER_KEY && waitkey != ESCAPE_KEY) diff --git a/src/misc_utilities/sortstate.cpp b/src/misc_utilities/sortstate.cpp index 7db2a33..d963176 100644 --- a/src/misc_utilities/sortstate.cpp +++ b/src/misc_utilities/sortstate.cpp @@ -25,7 +25,7 @@ #include #include "licenseplatecandidate.h" -#include "stateidentifier.h" +#include "../statedetection/state_detector.h" #include "utility.h" #include "support/filesystem.h" @@ -36,7 +36,7 @@ using namespace alpr; // Given a directory full of pre-cropped images, identify the state that each image belongs to. // This is used to sort our own positive image database as a first step before grabbing characters to use to train the OCR. -bool detectPlate( StateIdentifier* identifier, Mat frame); +bool detectPlate( StateDetector* identifier, Mat frame); int main( int argc, const char** argv ) { @@ -59,7 +59,7 @@ int main( int argc, const char** argv ) } Config config("us"); - StateIdentifier identifier(&config); + StateDetector identifier(config.country, config.runtimeBaseDir); if (DirectoryExists(outDir.c_str()) == false) { @@ -79,22 +79,17 @@ int main( int argc, const char** argv ) cout << fullpath << endl; frame = imread( fullpath.c_str() ); - PipelineData pipeline_data(frame, Rect(0, 0, frame.cols, frame.rows), &config); - identifier.recognize(&pipeline_data); - if (pipeline_data.region_confidence <= 20) + vector candidates = identifier.detect(frame.data, frame.elemSize(), frame.cols, frame.rows); + + if (candidates.size() > 0) { - pipeline_data.region_code = "zz"; - pipeline_data.region_confidence = 100; - } - else - { - cout << pipeline_data.region_confidence << " : " << pipeline_data.region_code; + cout << candidates[0].confidence << " : " << candidates[0].state_code; ostringstream convert; // stream used for the conversion convert << i; // insert the textual representation of 'Number' in the characters in the stream - string copyCommand = "cp \"" + fullpath + "\" " + outDir + pipeline_data.region_code + convert.str() + ".png"; + string copyCommand = "cp \"" + fullpath + "\" " + outDir + candidates[0].state_code + convert.str() + ".png"; system( copyCommand.c_str() ); waitKey(50); //while ((char) waitKey(50) != 'c') { } @@ -104,4 +99,4 @@ int main( int argc, const char** argv ) } } -bool detectPlate( StateIdentifier* identifier, Mat frame); +bool detectPlate( StateDetector* identifier, Mat frame); diff --git a/src/openalpr/CMakeLists.txt b/src/openalpr/CMakeLists.txt index 6ec14b9..e8556e6 100644 --- a/src/openalpr/CMakeLists.txt +++ b/src/openalpr/CMakeLists.txt @@ -12,8 +12,6 @@ set(lpr_source_files detection/detectormorph.cpp licenseplatecandidate.cpp utility.cpp - stateidentifier.cpp - featurematcher.cpp ocr.cpp postprocess/postprocess.cpp postprocess/regexrule.cpp @@ -51,6 +49,7 @@ set_target_properties(openalpr PROPERTIES SOVERSION ${OPENALPR_MAJOR_VERSION}) TARGET_LINK_LIBRARIES(openalpr support + statedetection ${OpenCV_LIBS} ${Tesseract_LIBRARIES} ) diff --git a/src/openalpr/alpr.cpp b/src/openalpr/alpr.cpp index 840b96b..a89ce21 100644 --- a/src/openalpr/alpr.cpp +++ b/src/openalpr/alpr.cpp @@ -68,6 +68,11 @@ namespace alpr return impl->recognize(imageBytes); } + AlprResults Alpr::recognize(std::vector imageBytes, std::vector regionsOfInterest) + { + return impl->recognize(imageBytes, regionsOfInterest); + } + AlprResults Alpr::recognize(unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight, std::vector regionsOfInterest) { return impl->recognize(pixelData, bytesPerPixel, imgWidth, imgHeight, regionsOfInterest); diff --git a/src/openalpr/alpr.h b/src/openalpr/alpr.h index 7146322..c94ed9b 100644 --- a/src/openalpr/alpr.h +++ b/src/openalpr/alpr.h @@ -132,8 +132,11 @@ namespace alpr // Recognize from an image on disk AlprResults recognize(std::string filepath); - // Recognize from byte data representing an encoded image (e.g., BMP, PNG, JPG, GIF etc). - AlprResults recognize(std::vector imageBytes); + // Recognize from byte data representing an encoded image (e.g., BMP, PNG, JPG, GIF etc). + AlprResults recognize(std::vector imageBytes); + + // Recognize from byte data representing an encoded image (e.g., BMP, PNG, JPG, GIF etc). + AlprResults recognize(std::vector imageBytes, std::vector regionsOfInterest); // Recognize from raw pixel data. AlprResults recognize(unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight, std::vector regionsOfInterest); diff --git a/src/openalpr/alpr_impl.cpp b/src/openalpr/alpr_impl.cpp index fc8ca37..85bf805 100644 --- a/src/openalpr/alpr_impl.cpp +++ b/src/openalpr/alpr_impl.cpp @@ -36,7 +36,7 @@ namespace alpr config = new Config(country, configFile, runtimeDir); plateDetector = ALPR_NULL_PTR; - stateIdentifier = ALPR_NULL_PTR; + stateDetector = ALPR_NULL_PTR; ocr = ALPR_NULL_PTR; prewarp = ALPR_NULL_PTR; @@ -69,8 +69,8 @@ namespace alpr if (plateDetector != ALPR_NULL_PTR) delete plateDetector; - if (stateIdentifier != ALPR_NULL_PTR) - delete stateIdentifier; + if (stateDetector != ALPR_NULL_PTR) + delete stateDetector; if (ocr != ALPR_NULL_PTR) delete ocr; @@ -97,6 +97,10 @@ namespace alpr 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, @@ -152,6 +156,7 @@ namespace alpr plateQueue.pop(); PipelineData pipeline_data(img, grayImg, plateRegion.rect, config); + pipeline_data.prewarp = prewarp; timespec platestarttime; getTimeMonotonic(&platestarttime); @@ -180,11 +185,15 @@ namespace alpr if (detectRegion) { - stateIdentifier->recognize(&pipeline_data); - if (pipeline_data.region_confidence > 0) + std::vector 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 = pipeline_data.region_code; - plateResult.regionConfidence = (int) pipeline_data.region_confidence; + plateResult.region = state_candidates[0].state_code; + plateResult.regionConfidence = (int) state_candidates[0].confidence; } } @@ -345,6 +354,16 @@ namespace alpr return this->recognize(img); } + AlprResults AlprImpl::recognize(std::vector imageBytes, std::vector regionsOfInterest) + { + cv::Mat img = cv::imdecode(cv::Mat(imageBytes), 1); + + std::vector rois = convertRects(regionsOfInterest); + + AlprFullDetails fullDetails = recognizeFullDetails(img, rois); + return fullDetails.results; + } + AlprResults AlprImpl::recognize( unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight, std::vector regionsOfInterest) { @@ -570,12 +589,12 @@ namespace alpr { this->detectRegion = detectRegion; - if (detectRegion && this->stateIdentifier == NULL) + if (detectRegion && this->stateDetector == NULL) { timespec startTime; getTimeMonotonic(&startTime); - this->stateIdentifier = new StateIdentifier(this->config); + this->stateDetector = new StateDetector(this->config->country, this->config->runtimeBaseDir); timespec endTime; getTimeMonotonic(&endTime); diff --git a/src/openalpr/alpr_impl.h b/src/openalpr/alpr_impl.h index f5fcbd9..6941fd1 100644 --- a/src/openalpr/alpr_impl.h +++ b/src/openalpr/alpr_impl.h @@ -35,7 +35,7 @@ #include "prewarp.h" #include "licenseplatecandidate.h" -#include "stateidentifier.h" +#include "../statedetection/state_detector.h" #include "segmentation/charactersegmenter.h" #include "ocr.h" @@ -76,6 +76,7 @@ namespace alpr AlprFullDetails recognizeFullDetails(cv::Mat img, std::vector regionsOfInterest); AlprResults recognize( std::vector imageBytes ); + AlprResults recognize( std::vector imageBytes, std::vector regionsOfInterest ); AlprResults recognize( unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight, std::vector regionsOfInterest ); AlprResults recognize( cv::Mat img ); AlprResults recognize( cv::Mat img, std::vector regionsOfInterest ); @@ -99,7 +100,7 @@ namespace alpr private: Detector* plateDetector; - StateIdentifier* stateIdentifier; + StateDetector* stateDetector; OCR* ocr; PreWarp* prewarp; diff --git a/src/openalpr/config.cpp b/src/openalpr/config.cpp index 35c24fa..1e8c99d 100644 --- a/src/openalpr/config.cpp +++ b/src/openalpr/config.cpp @@ -243,7 +243,10 @@ namespace alpr plateLinesSensitivityHorizontal = getFloat(ini, "", "plateline_sensitivity_horizontal", 0); ocrLanguage = getString(ini, "", "ocr_language", "none"); - + + postProcessRegexLetters = getString(ini, "", "postprocess_regex_letters", "\\pL"); + postProcessRegexNumbers = getString(ini, "", "postprocess_regex_numbers", "\\pN"); + ocrImageWidthPx = round(((float) templateWidthPx) * ocrImagePercent); ocrImageHeightPx = round(((float)templateHeightPx) * ocrImagePercent); stateIdImageWidthPx = round(((float)templateWidthPx) * stateIdImagePercent); diff --git a/src/openalpr/config.h b/src/openalpr/config.h index c4524e9..3767329 100644 --- a/src/openalpr/config.h +++ b/src/openalpr/config.h @@ -100,6 +100,9 @@ namespace alpr unsigned int postProcessMinCharacters; unsigned int postProcessMaxCharacters; + std::string postProcessRegexLetters; + std::string postProcessRegexNumbers; + bool debugGeneral; bool debugTiming; bool debugPrewarp; @@ -122,12 +125,13 @@ namespace alpr std::string getPostProcessRuntimeDir(); std::string getTessdataPrefix(); + std::string runtimeBaseDir; + private: float ocrImagePercent; float stateIdImagePercent; - - std::string runtimeBaseDir; + void loadCommonValues(std::string configFile); void loadCountryValues(std::string configFile, std::string country); diff --git a/src/openalpr/edges/scorekeeper.cpp b/src/openalpr/edges/scorekeeper.cpp index b1ad8f8..370c3b2 100644 --- a/src/openalpr/edges/scorekeeper.cpp +++ b/src/openalpr/edges/scorekeeper.cpp @@ -69,18 +69,19 @@ namespace alpr float total = getTotal(); + std::cout << "--------------------" << std::endl; + std::cout << "Total: " << total << std::endl; for (unsigned int i = 0; i < weight_ids.size(); i++) { float percent_of_total = (scores[i] * weights[i]) / total * 100; - std::cout << " - " << std::setw(longest_weight_id + 1) << std::left << weight_ids[i] << + std::cout << " - " << std::setw(longest_weight_id + 1) << std::left << weight_ids[i] << " Weighted Score: " << std::setw(10) << std::left << (scores[i] * weights[i]) << " Orig Score: " << std::setw(10) << std::left << scores[i] << " (" << percent_of_total << "% of total)" << std::endl; } - - std::cout << "Total: " << total << std::endl; + std::cout << "--------------------" << std::endl; } } \ No newline at end of file diff --git a/src/openalpr/licenseplatecandidate.cpp b/src/openalpr/licenseplatecandidate.cpp index a3e45b7..b8a107d 100644 --- a/src/openalpr/licenseplatecandidate.cpp +++ b/src/openalpr/licenseplatecandidate.cpp @@ -71,18 +71,30 @@ namespace alpr getTimeMonotonic(&startTime); - Mat originalCrop = pipeline_data->crop_gray; - + // Compute the transformation matrix to go from the current image to the new plate corners Transformation imgTransform(this->pipeline_data->grayImg, pipeline_data->crop_gray, expandedRegion); - - Size cropSize = imgTransform.getCropSize(pipeline_data->plate_corners, + Size cropSize = imgTransform.getCropSize(pipeline_data->plate_corners, Size(pipeline_data->config->ocrImageWidthPx, pipeline_data->config->ocrImageHeightPx)); Mat transmtx = imgTransform.getTransformationMatrix(pipeline_data->plate_corners, cropSize); - pipeline_data->crop_gray = imgTransform.crop(cropSize, transmtx); + + + // Crop the plate corners from the original color image (after un-applying prewarp) + vector projectedPoints = pipeline_data->prewarp->projectPoints(pipeline_data->plate_corners, true); + pipeline_data->color_deskewed = Mat::zeros(cropSize, pipeline_data->colorImg.type()); + std::vector deskewed_points; + deskewed_points.push_back(cv::Point2f(0,0)); + deskewed_points.push_back(cv::Point2f(pipeline_data->color_deskewed.cols,0)); + deskewed_points.push_back(cv::Point2f(pipeline_data->color_deskewed.cols,pipeline_data->color_deskewed.rows)); + deskewed_points.push_back(cv::Point2f(0,pipeline_data->color_deskewed.rows)); + cv::Mat color_transmtx = cv::getPerspectiveTransform(projectedPoints, deskewed_points); + cv::warpPerspective(pipeline_data->colorImg, pipeline_data->color_deskewed, color_transmtx, pipeline_data->color_deskewed.size()); + + // Make a grayscale copy as well for faster processing downstream + cv::cvtColor(pipeline_data->color_deskewed, pipeline_data->crop_gray, CV_BGR2GRAY); if (this->config->debugGeneral) - displayImage(config, "quadrilateral", pipeline_data->crop_gray); + displayImage(config, "quadrilateral", pipeline_data->color_deskewed); diff --git a/src/openalpr/ocr.cpp b/src/openalpr/ocr.cpp index c589b70..e05fcec 100644 --- a/src/openalpr/ocr.cpp +++ b/src/openalpr/ocr.cpp @@ -42,12 +42,13 @@ namespace alpr // Tesseract requires the prefix directory to be set as an env variable tesseract.Init(config->getTessdataPrefix().c_str(), config->ocrLanguage.c_str() ); tesseract.SetVariable("save_blob_choices", "T"); + tesseract.SetVariable("debug_file", "/dev/null"); tesseract.SetPageSegMode(PSM_SINGLE_CHAR); } OCR::~OCR() { - tesseract.Clear(); + tesseract.End(); } void OCR::performOCR(PipelineData* pipeline_data) diff --git a/src/openalpr/pipeline_data.h b/src/openalpr/pipeline_data.h index 9a42947..4fca140 100644 --- a/src/openalpr/pipeline_data.h +++ b/src/openalpr/pipeline_data.h @@ -7,6 +7,7 @@ #include "config.h" #include "textdetection/textline.h" #include "edges/scorekeeper.h" +#include "prewarp.h" namespace alpr { @@ -25,6 +26,8 @@ namespace alpr // Inputs Config* config; + PreWarp* prewarp; + cv::Mat colorImg; cv::Mat grayImg; cv::Rect regionOfInterest; @@ -33,6 +36,8 @@ namespace alpr cv::Mat crop_gray; + cv::Mat color_deskewed; + bool hasPlateBorder; cv::Mat plateBorderMask; std::vector textLines; diff --git a/src/openalpr/postprocess/postprocess.cpp b/src/openalpr/postprocess/postprocess.cpp index 2feb757..3d7bc5b 100644 --- a/src/openalpr/postprocess/postprocess.cpp +++ b/src/openalpr/postprocess/postprocess.cpp @@ -36,7 +36,7 @@ namespace alpr string region, pattern; while (infile >> region >> pattern) { - RegexRule* rule = new RegexRule(region, pattern); + RegexRule* rule = new RegexRule(region, pattern, config->postProcessRegexLetters, config->postProcessRegexNumbers); //cout << "REGION: " << region << " PATTERN: " << pattern << endl; if (rules.find(region) == rules.end()) @@ -170,7 +170,7 @@ namespace alpr for (int i = 0; i < letters.size(); i++) { if (letters[i].size() > 0) - sort(letters[i].begin(), letters[i].end(), letterCompare); + std::stable_sort(letters[i].begin(), letters[i].end(), letterCompare); } if (this->config->debugPostProcess) @@ -386,12 +386,6 @@ namespace alpr return true; } - bool wordCompare( const PPResult &left, const PPResult &right ) - { - if (left.totalscore < right.totalscore) - return false; - return true; - } bool letterCompare( const Letter &left, const Letter &right ) { diff --git a/src/openalpr/postprocess/postprocess.h b/src/openalpr/postprocess/postprocess.h index ee25c64..5eddc08 100644 --- a/src/openalpr/postprocess/postprocess.h +++ b/src/openalpr/postprocess/postprocess.h @@ -53,7 +53,6 @@ namespace alpr std::vector letter_details; }; - bool wordCompare( const PPResult &left, const PPResult &right ); bool letterCompare( const Letter &left, const Letter &right ); @@ -77,7 +76,7 @@ namespace alpr private: Config* config; - //void getTopN(); + void findAllPermutations(std::string templateregion, int topn); bool analyzePermutation(std::vector letterIndices, std::string templateregion, int topn); diff --git a/src/openalpr/postprocess/regexrule.cpp b/src/openalpr/postprocess/regexrule.cpp index 57bd619..aa8226e 100644 --- a/src/openalpr/postprocess/regexrule.cpp +++ b/src/openalpr/postprocess/regexrule.cpp @@ -28,7 +28,7 @@ tthread::mutex regexrule_mutex_m; namespace alpr { - RegexRule::RegexRule(string region, string pattern) + RegexRule::RegexRule(string region, string pattern, std::string letters_regex, std::string numbers_regex) //: re2_regex("") { this->original = pattern; @@ -80,11 +80,11 @@ namespace alpr } else if (utf_character == "@") { - regexval << "\\pL"; + regexval << letters_regex; } else if (utf_character == "#") { - regexval << "\\pN"; + regexval << numbers_regex; } else if ((utf_character == "*") || (utf_character == "+")) { diff --git a/src/openalpr/postprocess/regexrule.h b/src/openalpr/postprocess/regexrule.h index d071f61..d369a92 100644 --- a/src/openalpr/postprocess/regexrule.h +++ b/src/openalpr/postprocess/regexrule.h @@ -33,7 +33,7 @@ namespace alpr class RegexRule { public: - RegexRule(std::string region, std::string pattern); + RegexRule(std::string region, std::string pattern, std::string letters_regex, std::string numbers_regex); virtual ~RegexRule(); bool match(std::string text); diff --git a/src/openalpr/segmentation/charactersegmenter.cpp b/src/openalpr/segmentation/charactersegmenter.cpp index 2871d10..7a14956 100644 --- a/src/openalpr/segmentation/charactersegmenter.cpp +++ b/src/openalpr/segmentation/charactersegmenter.cpp @@ -159,7 +159,6 @@ namespace alpr getTimeMonotonic(&startTime); filterEdgeBoxes(pipeline_data->thresholds, candidateBoxes, medianCharWidth, avgCharHeight); - candidateBoxes = filterMostlyEmptyBoxes(pipeline_data->thresholds, candidateBoxes); candidateBoxes = combineCloseBoxes(candidateBoxes, medianCharWidth); candidateBoxes = filterMostlyEmptyBoxes(pipeline_data->thresholds, candidateBoxes); @@ -260,7 +259,7 @@ namespace alpr vector CharacterSegmenter::getBestCharBoxes(Mat img, vector charBoxes, float avgCharWidth) { - float MAX_SEGMENT_WIDTH = avgCharWidth * 1.65; + float MAX_SEGMENT_WIDTH = avgCharWidth * config->segmentationMaxCharWidthvsAverage; // This histogram is based on how many char boxes (from ALL of the many thresholded images) are covering each column // Makes a sort of histogram from all the previous char boxes. Figures out the best fit from that. diff --git a/src/openalpr/segmentation/charactersegmenter.h b/src/openalpr/segmentation/charactersegmenter.h index 76ec3f4..29376b0 100644 --- a/src/openalpr/segmentation/charactersegmenter.h +++ b/src/openalpr/segmentation/charactersegmenter.h @@ -74,7 +74,6 @@ namespace alpr void cleanCharRegions(std::vector thresholds, std::vector charRegions); void cleanBasedOnColor(std::vector thresholds, cv::Mat colorMask, std::vector charRegions); - void cleanMostlyFullBoxes(std::vector thresholds, const std::vector charRegions); std::vector filterMostlyEmptyBoxes(std::vector thresholds, const std::vector charRegions); void filterEdgeBoxes(std::vector thresholds, const std::vector charRegions, float avgCharWidth, float avgCharHeight); diff --git a/src/openalpr/stateidentifier.cpp b/src/openalpr/stateidentifier.cpp deleted file mode 100644 index 9e02ffb..0000000 --- a/src/openalpr/stateidentifier.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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 "stateidentifier.h" - - -using namespace cv; -using namespace std; - -namespace alpr -{ - - StateIdentifier::StateIdentifier(Config* config) - { - this->config = config; - - featureMatcher = new FeatureMatcher(config); - - if (featureMatcher->isLoaded() == false) - { - cout << "Can not create detector or descriptor extractor or descriptor matcher of given types" << endl; - return; - } - - featureMatcher->loadRecognitionSet(config->country); - } - - StateIdentifier::~StateIdentifier() - { - delete featureMatcher; - } - - - // Attempts to recognize the plate. Returns a confidence level. Updates the region code and confidence - // If region is found, returns true. - bool StateIdentifier::recognize(PipelineData* pipeline_data) - { - timespec startTime; - getTimeMonotonic(&startTime); - - Mat plateImg = Mat(pipeline_data->grayImg, pipeline_data->regionOfInterest); - - resize(plateImg, plateImg, getSizeMaintainingAspect(plateImg, config->stateIdImageWidthPx, config->stateIdimageHeightPx)); - - - Mat debugImg(plateImg.size(), plateImg.type()); - plateImg.copyTo(debugImg); - vector matchesArray(featureMatcher->numTrainingElements()); - - RecognitionResult result = featureMatcher->recognize(plateImg, true, &debugImg, true, matchesArray ); - - if (this->config->debugStateId) - { - displayImage(config, "State Identifier1", plateImg); - displayImage(config, "State Identifier", debugImg); - cout << result.haswinner << " : " << result.confidence << " : " << result.winner << endl; - } - - if (config->debugTiming) - { - timespec endTime; - getTimeMonotonic(&endTime); - cout << "State Identification Time: " << diffclock(startTime, endTime) << "ms." << endl; - } - - if (result.haswinner == false) - return 0; - - pipeline_data->region_code = result.winner; - pipeline_data->region_confidence = result.confidence; - - if (result.confidence >= 10) - return true; - - return false; - } - -} \ No newline at end of file diff --git a/src/openalpr/support/platform.cpp b/src/openalpr/support/platform.cpp index 185e257..a8db239 100644 --- a/src/openalpr/support/platform.cpp +++ b/src/openalpr/support/platform.cpp @@ -30,8 +30,9 @@ namespace alpr return directory; #else char buffer[2048]; + memset(buffer, 0, sizeof(buffer)); - readlink("/proc/self/exe", buffer, 2048); + readlink("/proc/self/exe", buffer, sizeof(buffer)); std::stringstream ss; ss << buffer; diff --git a/src/openalpr/support/platform.h b/src/openalpr/support/platform.h index 07b15a4..4100ecd 100644 --- a/src/openalpr/support/platform.h +++ b/src/openalpr/support/platform.h @@ -1,7 +1,7 @@ #ifndef OPENALPR_PLATFORM_H #define OPENALPR_PLATFORM_H -#include +#include #include #ifdef WINDOWS diff --git a/src/statedetection/CMakeLists.txt b/src/statedetection/CMakeLists.txt new file mode 100644 index 0000000..405f8af --- /dev/null +++ b/src/statedetection/CMakeLists.txt @@ -0,0 +1,27 @@ + + + +set(statedetector_source_files + state_detector.cpp + featurematcher.cpp + state_detector_impl.cpp +) + +if (WIN32) + add_library(statedetection STATIC ${statedetector_source_files} ) +ELSE() + add_library(statedetection SHARED ${statedetector_source_files} ) +ENDIF() + +set_target_properties(statedetection PROPERTIES SOVERSION ${OPENALPR_MAJOR_VERSION}) + +TARGET_LINK_LIBRARIES(statedetection + ${OpenCV_LIBS} + support +) + + +install (FILES state_detector.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include) +install (TARGETS statedetection DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) + + diff --git a/src/openalpr/featurematcher.cpp b/src/statedetection/featurematcher.cpp similarity index 92% rename from src/openalpr/featurematcher.cpp rename to src/statedetection/featurematcher.cpp index e03275e..42f1b33 100644 --- a/src/openalpr/featurematcher.cpp +++ b/src/statedetection/featurematcher.cpp @@ -29,10 +29,8 @@ namespace alpr //const int DEFAULT_TRAINING_FEATURES = 305; const float MAX_DISTANCE_TO_MATCH = 100.0f; - FeatureMatcher::FeatureMatcher(Config* config) + FeatureMatcher::FeatureMatcher() { - this->config = config; - //this->descriptorMatcher = DescriptorMatcher::create( "BruteForce-HammingLUT" ); this->descriptorMatcher = new BFMatcher(NORM_HAMMING, false); @@ -152,8 +150,8 @@ namespace alpr // We assume that license plates won't be upside-down or backwards. So expect lines to be closely parallel void FeatureMatcher::crisscrossFiltering(const vector queryKeypoints, const vector inputMatches, vector &outputMatches) { - Rect crissCrossAreaVertical(0, 0, config->stateIdImageWidthPx, config->stateIdimageHeightPx * 2); - Rect crissCrossAreaHorizontal(0, 0, config->stateIdImageWidthPx * 2, config->stateIdimageHeightPx); + Rect crissCrossAreaVertical(0, 0, w, h * 2); + Rect crissCrossAreaHorizontal(0, 0, w * 2, h); for (unsigned int i = 0; i < billMapping.size(); i++) { @@ -175,8 +173,8 @@ namespace alpr KeyPoint tkp = trainingImgKeypoints[i][matchesForOnePlate[j].trainIdx]; KeyPoint qkp = queryKeypoints[matchesForOnePlate[j].queryIdx]; - vlines.push_back(LineSegment(tkp.pt.x, tkp.pt.y + config->stateIdimageHeightPx, qkp.pt.x, qkp.pt.y)); - hlines.push_back(LineSegment(tkp.pt.x, tkp.pt.y, qkp.pt.x + config->stateIdImageWidthPx, qkp.pt.y)); + vlines.push_back(LineSegment(tkp.pt.x, tkp.pt.y + h, qkp.pt.x, qkp.pt.y)); + hlines.push_back(LineSegment(tkp.pt.x, tkp.pt.y, qkp.pt.x + w, qkp.pt.y)); matchIdx.push_back(j); } @@ -215,8 +213,8 @@ namespace alpr if (mostIntersectionsIndex >= 0) { - if (this->config->debugStateId) - cout << "Filtered intersection! " << billMapping[i] << endl; +// if (this->config->debugStateId) +// cout << "Filtered intersection! " << billMapping[i] << endl; vlines.erase(vlines.begin() + mostIntersectionsIndex); hlines.erase(hlines.begin() + mostIntersectionsIndex); matchIdx.erase(matchIdx.begin() + mostIntersectionsIndex); @@ -232,10 +230,10 @@ namespace alpr } // Returns true if successful, false otherwise - bool FeatureMatcher::loadRecognitionSet(string country) + bool FeatureMatcher::loadRecognitionSet(string directory, string country) { std::ostringstream out; - out << config->getKeypointsRuntimeDir() << "/" << country << "/"; + out << directory << "/keypoints/" << country << "/"; string country_dir = out.str(); if (DirectoryExists(country_dir.c_str())) @@ -253,7 +251,6 @@ namespace alpr // convert to gray and resize to the size of the templates cvtColor(img, img, CV_BGR2GRAY); - resize(img, img, getSizeMaintainingAspect(img, config->stateIdImageWidthPx, config->stateIdimageHeightPx)); if( img.empty() ) { @@ -290,6 +287,9 @@ namespace alpr { RecognitionResult result; + this->w = queryImg.cols; + this->h = queryImg.rows; + result.haswinner = false; result.confidence = 0; @@ -381,13 +381,13 @@ namespace alpr } } - if (this->config->debugStateId) - { - for (unsigned int i = 0; i < billMapping.size(); i++) - { - cout << billMapping[i] << " : " << bill_match_counts[i] << endl; - } - } +// if (this->config->debugStateId) +// { +// for (unsigned int i = 0; i < billMapping.size(); i++) +// { +// cout << billMapping[i] << " : " << bill_match_counts[i] << endl; +// } +// } return result; } diff --git a/src/openalpr/featurematcher.h b/src/statedetection/featurematcher.h similarity index 94% rename from src/openalpr/featurematcher.h rename to src/statedetection/featurematcher.h index 98c5938..1319b9a 100644 --- a/src/openalpr/featurematcher.h +++ b/src/statedetection/featurematcher.h @@ -44,20 +44,19 @@ namespace alpr { public: - FeatureMatcher(Config* config); + FeatureMatcher(); virtual ~FeatureMatcher(); RecognitionResult recognize( const cv::Mat& queryImg, bool drawOnImage, cv::Mat* outputImage, bool debug_on, std::vector debug_matches_array ); - bool loadRecognitionSet(std::string country); + bool loadRecognitionSet(std::string runtime_dir, std::string country); bool isLoaded(); int numTrainingElements(); private: - Config* config; cv::Ptr descriptorMatcher; cv::Ptr detector; @@ -74,6 +73,9 @@ namespace alpr void surfStyleMatching( const cv::Mat& queryDescriptors, std::vector queryKeypoints, std::vector& matches12 ); + + int w; + int h; }; } diff --git a/src/statedetection/state_detector.cpp b/src/statedetection/state_detector.cpp new file mode 100644 index 0000000..8c27045 --- /dev/null +++ b/src/statedetection/state_detector.cpp @@ -0,0 +1,52 @@ +/* + * 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 "state_detector.h" +#include "state_detector_impl.h" + +using namespace std; + +namespace alpr { + + StateDetector::StateDetector(const std::string country, const std::string runtimeDir) { + impl = new StateDetectorImpl(country, runtimeDir); + } + + StateDetector::~StateDetector() { + delete impl; + } + + bool StateDetector::isLoaded() { + return impl->isLoaded(); + } + + void StateDetector::setTopN(int topN) { + impl->setTopN(topN); + } + + vector StateDetector::detect(vector imageBytes) { + return impl->detect(imageBytes); + } + + vector StateDetector::detect(unsigned char *pixelData, int bytesPerPixel, int imgWidth, + int imgHeight) { + return impl->detect(pixelData, bytesPerPixel, imgWidth, imgHeight); + } + +} diff --git a/src/statedetection/state_detector.h b/src/statedetection/state_detector.h new file mode 100644 index 0000000..4cd29b6 --- /dev/null +++ b/src/statedetection/state_detector.h @@ -0,0 +1,55 @@ +/* + * 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 . +*/ + +#ifndef OPENALPR_STATE_DETECTOR_H +#define OPENALPR_STATE_DETECTOR_H + +#include +#include + +namespace alpr { + + struct StateCandidate + { + std::string state_code; + float confidence; + }; + + class StateDetectorImpl; + class StateDetector { + + public: + StateDetector(const std::string country, const std::string runtimeDir); + virtual ~StateDetector(); + + bool isLoaded(); + + // Maximum number of candidates to return + void setTopN(int topN); + + // Given an image of a license plate, provide the likely state candidates + std::vector detect(std::vector imageBytes); + std::vector detect(unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight); + + StateDetectorImpl* impl; + }; + +} + +#endif //OPENALPR_STATE_DETECTOR_H diff --git a/src/statedetection/state_detector_impl.cpp b/src/statedetection/state_detector_impl.cpp new file mode 100644 index 0000000..e4daa66 --- /dev/null +++ b/src/statedetection/state_detector_impl.cpp @@ -0,0 +1,83 @@ +/* + * 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 "state_detector_impl.h" + +namespace alpr +{ + StateDetectorImpl::StateDetectorImpl(const std::string country, const std::string runtimeDir) + { + + + if (featureMatcher.isLoaded() == false) + { + std::cout << "Can not create detector or descriptor extractor or descriptor matcher of given types" << std::endl; + return; + } + + featureMatcher.loadRecognitionSet(runtimeDir, country); + } + + StateDetectorImpl::~StateDetectorImpl() { } + + bool StateDetectorImpl::isLoaded() { + return false; + } + + void StateDetectorImpl::setTopN(int topN) { + } + + std::vector StateDetectorImpl::detect(std::vector imageBytes) { + cv::Mat img = cv::imdecode(cv::Mat(imageBytes), 1); + + return this->detect(img); + } + + std::vector StateDetectorImpl::detect(unsigned char *pixelData, int bytesPerPixel, int imgWidth, + int imgHeight) { + int arraySize = imgWidth * imgHeight * bytesPerPixel; + cv::Mat imgData = cv::Mat(arraySize, 1, CV_8U, pixelData); + cv::Mat img = imgData.reshape(bytesPerPixel, imgHeight); + + return this->detect(img); + + } + + std::vector StateDetectorImpl::detect(cv::Mat image) { + std::vector results; + + + cv::Mat debugImg(image.size(), image.type()); + image.copyTo(debugImg); + std::vector matchesArray(featureMatcher.numTrainingElements()); + + RecognitionResult result = featureMatcher.recognize(image, true, &debugImg, true, matchesArray ); + + if (result.haswinner == false) + return results; + + StateCandidate top_candidate; + top_candidate.confidence = result.confidence; + top_candidate.state_code = result.winner; + + results.push_back(top_candidate); + + return results; + } +} \ No newline at end of file diff --git a/src/openalpr/stateidentifier.h b/src/statedetection/state_detector_impl.h similarity index 52% rename from src/openalpr/stateidentifier.h rename to src/statedetection/state_detector_impl.h index c7680c0..273fbcb 100644 --- a/src/openalpr/stateidentifier.h +++ b/src/statedetection/state_detector_impl.h @@ -17,38 +17,33 @@ * along with this program. If not, see . */ -#ifndef OPENALPR_STATEIDENTIFIER_H -#define OPENALPR_STATEIDENTIFIER_H +#ifndef SRC_STATE_DETECTOR_IMPL_H +#define SRC_STATE_DETECTOR_IMPL_H -#include "opencv2/imgproc/imgproc.hpp" -#include "constants.h" +#include "state_detector.h" #include "featurematcher.h" -#include "utility.h" -#include "config.h" -#include "pipeline_data.h" +#include +#include -namespace alpr -{ - - class StateIdentifier - { +namespace alpr { + class StateDetectorImpl { public: - StateIdentifier(Config* config); - virtual ~StateIdentifier(); + StateDetectorImpl(const std::string country, const std::string runtimeDir); + virtual ~StateDetectorImpl(); - bool recognize(PipelineData* pipeline_data); + bool isLoaded(); - //int confidence; + // Maximum number of candidates to return + void setTopN(int topN); - protected: - Config* config; - - private: - - FeatureMatcher* featureMatcher; + std::vector detect(std::vector imageBytes); + std::vector detect(unsigned char* pixelData, int bytesPerPixel, int imgWidth, int imgHeight); + std::vector detect(cv::Mat image); + FeatureMatcher featureMatcher; }; } -#endif // OPENALPR_STATEIDENTIFIER_H + +#endif //SRC_STATE_DETECTOR_IMPL_H diff --git a/src/tests/test_regex.cpp b/src/tests/test_regex.cpp index 1c516dc..fe76582 100644 --- a/src/tests/test_regex.cpp +++ b/src/tests/test_regex.cpp @@ -11,7 +11,7 @@ using namespace alpr; TEST_CASE( "ASCII tests", "[Regex]" ) { - RegexRule rule1("us", "@@@####"); + RegexRule rule1("us", "@@@####", "[A-Za-z]", "[0-9]"); REQUIRE( rule1.match("123ABCD") == false); REQUIRE( rule1.match("123ABC") == false); @@ -28,7 +28,7 @@ TEST_CASE( "ASCII tests", "[Regex]" ) { REQUIRE( rule1.match("AAA1111") == true); REQUIRE( rule1.match("zzz1111") == true); - RegexRule rule2("us", "[ABC]@@####"); + RegexRule rule2("us", "[ABC]@@####", "[A-Z]", "[0-9]"); REQUIRE( rule2.match("ZBC1234") == false); REQUIRE( rule2.match("DBC1234") == false); @@ -38,7 +38,7 @@ TEST_CASE( "ASCII tests", "[Regex]" ) { REQUIRE( rule2.match("BAA1111") == true); REQUIRE( rule2.match("CAA1111") == true); - RegexRule rule3("us", "[A]@@###[12]"); + RegexRule rule3("us", "[A]@@###[12]", "[A-Z]", "[0-9]"); REQUIRE( rule3.match("ZBC1234") == false); REQUIRE( rule3.match("ZBC1231") == false); @@ -48,7 +48,7 @@ TEST_CASE( "ASCII tests", "[Regex]" ) { REQUIRE( rule3.match("ABC1232") == true); - RegexRule rule4("us", "[A-C][E-G]1111"); + RegexRule rule4("us", "[A-C][E-G]1111", "[A-Z]", "[0-9]"); REQUIRE( rule4.match("DG1111") == false); REQUIRE( rule4.match("AD1111") == false); @@ -61,7 +61,7 @@ TEST_CASE( "ASCII tests", "[Regex]" ) { REQUIRE( rule4.match("BF1111") == true); REQUIRE( rule4.match("BG1111") == true); - RegexRule rule5("us", "\\d\\d\\D\\D"); + RegexRule rule5("us", "\\d\\d\\D\\D", "[A-Z]", "[0-9]"); REQUIRE( rule5.match("AA11") == false); REQUIRE( rule5.match("11AA") == true); @@ -69,7 +69,7 @@ TEST_CASE( "ASCII tests", "[Regex]" ) { TEST_CASE( "Unicode tests", "[Regex]" ) { - RegexRule rule1("us", "@@@####"); + RegexRule rule1("us", "@@@####", "\\pL", "\\pN"); REQUIRE( rule1.match("123与与与下") == false); REQUIRE( rule1.match("与万12345") == false); @@ -78,7 +78,7 @@ TEST_CASE( "Unicode tests", "[Regex]" ) { REQUIRE( rule1.match("与万口1234") == true); - RegexRule rule2("us", "[십팔]@@####"); + RegexRule rule2("us", "[십팔]@@####", "\\pL", "\\pN"); REQUIRE( rule2.match("123与与与下") == false); REQUIRE( rule2.match("与万12345") == false); @@ -93,7 +93,7 @@ TEST_CASE( "Unicode tests", "[Regex]" ) { TEST_CASE( "Invalid tests", "[Regex]" ) { - RegexRule rule1("us", "[A@@####"); + RegexRule rule1("us", "[A@@####", "\\pL", "\\pN"); REQUIRE( rule1.match("123ABCD") == false); REQUIRE( rule1.match("123ABC") == false); @@ -103,6 +103,6 @@ TEST_CASE( "Invalid tests", "[Regex]" ) { REQUIRE( rule1.match("AAA1111") == false); REQUIRE( rule1.match("zzz1111") == false); - RegexRule rule2("us", "A####]"); + RegexRule rule2("us", "A####]", "\\pL", "\\pN"); REQUIRE( rule2.match("A1234") == false); } \ No newline at end of file