Commit ab9a8724 authored by nextime's avatar nextime

Migrate from svn to git...

parents
This diff is collapsed.
#/***********************************************************************
# This file is part of LiPRec, License Plate REcognition.
#
# Copyright (C) 2012 Franco (nextime) Lanza <nextime@nexlab.it>
#
# LiPRec is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# LiPRec 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 LiPRec. If not, see <http://www.gnu.org/licenses/>.
#************************************************************************/
OBJECTS =libliprec.o libliprec.so liprec
CFLAGS= -O3 -march=native -Wall
CPPFLAGS= -fpermissive -O3 -march=native -Wall
CPPFLAGS+=$(shell pkg-config --cflags opencv)
CPPFLAGS=-L. -L/usr/lib -I/usr/include
LDFLAGS=-ltesseract
LDFLAGS+=$(shell pkg-config --cflags --libs opencv)
all: ${OBJECTS}
debug: CFLAGS+=-D__DEBUG -g
debug: CPPFLAGS+=-D__DEBUG
debug: clean all
verbosedebug: CFLAGS+=-D__SHOWIMAGES
verbosedebug: CPPFLAGS+=-D__SHOWIMAGES
verbosedebug: debug
libliprec.o: libliprec.cpp
$(CXX) libliprec.cpp -fPIC -c -o libliprec.o $(CPPFLAGS)
libliprec.so:
$(CXX) -o libliprec.so -Wall -shared libliprec.o $(LDFLAGS)
liprec: liprec.cpp
$(CXX) liprec.cpp -o liprec -lliprec ${LDFLAGS} $(CPPFLAGS)
lib_install:
install -m 0644 libliprec.so /usr/lib
install -m 0644 liprec.h /usr/include
ldconfig
install: lib_install
install -m 0755 liprec /usr/bin/
clean:
rm -f $(OBJECTS)
This diff is collapsed.
/***********************************************************************
This file is part of LiPRec, License Plate REcognition.
Copyright (C) 2012 Franco (nextime) Lanza <nextime@nexlab.it>
LiPRec is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
LiPRec 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 LiPRec. If not, see <http://www.gnu.org/licenses/>.
************************************************************************/
#include "liprec.h"
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "optionparser.h"
using namespace liprec;
using namespace std;
using namespace cv;
enum optionIndex { OPT_UNKNOWN, OPT_HELP, OPT_DEBUG, OPT_GUI, OPT_PAUSE};
const option::Descriptor usage[] =
{
{OPT_UNKNOWN, 0,"", "" ,option::Arg::None, "USAGE: liprec [options] <video_file|image_file|video uri>\n\n"
"Options:" },
{OPT_HELP, 0,"h","help",option::Arg::None, " -h, --help \tPrint usage and exit." },
{OPT_DEBUG, 0,"d","debug",option::Arg::Optional, " -d[level], --debug[=level] \tSet debug level."},
{OPT_GUI, 0,"g","gui",option::Arg::None, " -g, --gui \tshow graphic UI." },
{OPT_PAUSE, 0,"p","",option::Arg::None, " -p \tpause video on plate detected\n"},
{OPT_UNKNOWN, 0,"", "" ,option::Arg::None, "\nExamples:\n"
" liprec -d file1.mjpeg\n"
" liprec http://<ip_addr>/img/video.h264\n"
" liprec -g file.jpg\n" },
{0,0,0,0,0,0}
};
int main(int argc, char* argv[])
{
int debug_level=0;
int use_gui=0;
int pause=0;
Mat frame;
LiPRec plateDetector;
argc-=(argc>0); argv+=(argc>0); // skip program name argv[0] if present
option::Stats stats(usage, argc, argv);
option::Option options[stats.options_max], buffer[stats.buffer_max];
option::Parser parse(usage, argc, argv, options, buffer);
if(parse.error())
{
cout << "Error parsing options\n";
return -1;
}
double imgnum=0;
#ifdef __DEBUG
debug_level=1;
#endif
#ifdef __SHOWIMAGES
debug_level=2;
#endif
//if( argc != 2) {
if(parse.nonOptionsCount()<1) {
cout << "You must specify a file to load\n\n";
option::printUsage(std::cout, usage);
return -1;
}
if (options[OPT_HELP]) {
option::printUsage(std::cout, usage);
return 0;
}
for (int i = 0; i < parse.optionsCount(); ++i) {
option::Option& opt = buffer[i];
switch(opt.index()) {
case OPT_HELP:
option::printUsage(std::cout, usage);
return 0;
case OPT_DEBUG:
debug_level=1;
if(opt.arg && atoi(opt.arg)<3)
{
debug_level=atoi(opt.arg);
if(debug_level > 0)
cout << "Debug level is " << debug_level << endl;
if(debug_level > 1)
use_gui=1;
}
break;
case OPT_GUI:
use_gui=1;
break;
case OPT_PAUSE:
pause=1;
break;
}
}
//VideoCapture cap(argv[1]);
VideoCapture cap(parse.nonOption(0));
if(!cap.isOpened()) {
cout << "Cannot open file " << argv[1] << endl;
return -1;
}
if(use_gui) {
cv::namedWindow("LiPRec", 0);
}
for(;;) {
//#ifdef __DEBUG
if(debug_level) {
imgnum++;
cout << "Working in frame # " << imgnum << endl;
}
if(cap.grab())
cap.retrieve(frame);
else
{
cout << "Video is over\n";
cv::waitKey(0);
break;
}
PlatesImage plates;
//plateDetector.optimizeImage(frame, frame);
plateDetector.detectPlates(frame, &plates);
if(debug_level) {
cout << "Plates vector size: " << plates.plates.size() << endl;
}
if(use_gui) {
cv::imshow("LiPRec", plates.image);
}
if(plates.plates.size() > 0) {
for(unsigned int i=0;i<plates.plates.size();i++) {
cout << "** Plates found: " << plates.plates[i].platetxt;
cout << " (confidence:" << plates.plates[i].confidence << ")" << endl;
}
if(pause) {
if(use_gui) {
cv::waitKey();
}
else {
std::cout << "Press enter to continue: ";
std::cin >> pause;
}
}
}
if(debug_level>1 || use_gui) {
if(cv::waitKey(30) >= 0) break;
}
}
return 0;
}
/***********************************************************************
This file is part of LiPRec, License Plate REcognition.
Copyright (C) 2012 Franco (nextime) Lanza <nextime@nexlab.it>
LiPRec is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
LiPRec 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 LiPRec. If not, see <http://www.gnu.org/licenses/>.
************************************************************************/
#ifndef __LIPREC_H__
#define __LIPREC_H__
#define LIPREC_OPTIMIZATION_GREY_BASIC (1)
#define LIPREC_OPTIMIZATION_HSV_BASIC (2)
#define LIPREC_OPTIMIZATION_GREY_DEEP (3)
#define LIPREC_OPTIMIZATION_HSV_DEEP (4)
#define LIPREC_CONTOUR_THRESHOLD (1)
#define LIPREC_CONTOUR_AUTOTHRESHOLD (2)
#define LIPREC_CONTOUR_CANNY (3)
#define LIPREC_PLATECON_THRESHOLD (1)
#define LIPREC_PLATECON_AUTOTHRESHOLD (2)
#define LIPREC_PLATECON_CANNY (3)
#ifdef __cplusplus
#include "opencv2/opencv.hpp"
//#include "opencv2/highgui/highgui.hpp"
#include "tesseract/baseapi.h"
#define TESSERACT_PAGETYPE_BLOCK (tesseract::PSM_SINGLE_BLOCK)
#define TESSERACT_PAGETYPE_SINGLE_VERT (tesseract::PSM_SINGLE_BLOCK_VERT_TEXT)
#define TESSERACT_PAGETYPE_SINGLE_CHAR (tesseract::PSM_SINGLE_CHAR)
namespace liprec
{
class Plate {
public:
cv::Mat contours;
cv::Mat ocrimage;
cv::Rect rect;
cv::String platetxt;
int confidence;
//~Plate();
};
class PlatesImage {
public:
cv::Mat image;
cv::Mat optimizedimage;
cv::Mat contours;
std::vector<Plate> plates;
//~PlatesImage();
};
class LiPRec {
public:
LiPRec(int optimization=LIPREC_OPTIMIZATION_GREY_BASIC,
int contour=LIPREC_CONTOUR_CANNY,
int platecont=LIPREC_PLATECON_THRESHOLD,
tesseract::PageSegMode pagetype=TESSERACT_PAGETYPE_BLOCK,
int min_ocr_confidence=33
);
void optimizeImage(const cv::Mat &inimg, cv::Mat &outimg);
void detectPlates(cv::Mat &img, PlatesImage* plates,
int min_area=600, int max_area=6000);
void detectPlates(cv::Mat &img, cv::Mat &optimizedimage, PlatesImage* plates,
int min_area=600, int max_area=6000);
void setThreshold(int min=128, int max=255);
void setAutothreshold(int size=21);
void setPlateThreshold(int min, int max=255);
void setPlateAutothreshold(int size=11);
void setPerimeterConstant(int val=35);
virtual ~LiPRec(); // descructor
private:
int opt, cont, pcont, min_confidence;
int thr_min, thr_max, athr_size;
int thrp_min, thrp_max, athrp_size;
float perimeter_constant;
tesseract::TessBaseAPI *OCR;
tesseract::PageSegMode ocr_ptype;
void startOCR(tesseract::PageSegMode pagetype);
void maximizeContrast(cv::Mat &img);
void extractV(const cv::Mat &inimg, cv::Mat &outimg);
void _detectPlates(cv::Mat &img, cv::Mat &optimizedimage, PlatesImage* plates,
int min_area=600, int max_area=6000);
};
}
#endif // __cplusplus
#endif // #ifndef __LIPREC_H
/***********************************************************************
This file is part of LiPRec, License Plate REcognition.
Copyright (C) 2012 Franco (nextime) Lanza <nextime@nexlab.it>
LiPRec is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
LiPRec 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 LiPRec. If not, see <http://www.gnu.org/licenses/>.
************************************************************************/
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "tesseract/baseapi.h"
using namespace cv;
using namespace std;
using namespace tesseract;
int high_switch_value = 35;
float perimeter_constant = high_switch_value/1000.0;
int minimum_area= 600.0;
int maximum_area= 6000.0;
int threshold_val = 128;
int threshold2_val = 130;
int athr_size = 21;
int athr2_size = 11;
void switch_callback_h( int position, void* udata ){
perimeter_constant = position/1000.0;
}
/* NOTE: only for adaptive threshold */
//void athr_callback(int position, void* udata){
// // we need a non-pair value for adaptive threshold!
// if(*((int*)udata)%2==0)
// *((int*)udata)+=1;
//}
int main(int argc, char* argv[]) {
const char* name_main = "Plate Recognition";
const char* name_edge = "Edge Window";
#if defined(__DMDEBUG)
const char* name_mask = "Mask Window";
#endif
const char* name_bwframe = "BW Window";
const char* name_crop = "CROP Window";
const char* name_ctrl = "Control Window";
unsigned int i;
Mat frame;
Mat bwframe;
Mat edge;
TessBaseAPI *OCR = new TessBaseAPI();
if(OCR->Init(NULL, "eng")) {
cout << "Could not initialize tesseract OCR\n";
return -1;
}
if( argc != 2) {
cout << "You must specify a file to load\n";
return -1;
}
VideoCapture cap(argv[1]);
if(!cap.isOpened()) {
cout << "Cannot open file " << argv[1] << endl;
return -1;
}
namedWindow(name_main, 0);
namedWindow(name_edge, 0);
#if defined(__DMDEBUG)
namedWindow(name_mask, 0);
#endif
namedWindow(name_bwframe, 0);
namedWindow(name_ctrl, 0);
//#if defined(__DMDEBUG)
namedWindow(name_crop, 0);
namedWindow("AAA", 0);
//#endif
createTrackbar( "Contour perimeter", name_ctrl, &high_switch_value, 100, switch_callback_h, NULL );
createTrackbar( "Min area", name_ctrl, &minimum_area, 100000, NULL, NULL);
createTrackbar( "Max area", name_ctrl, &maximum_area, 100000, NULL, NULL);
createTrackbar( "Threshold", name_ctrl, &threshold_val, 255, NULL, NULL);
//createTrackbar( "thr size", name_ctrl, &athr_size, 255, athr_callback, &athr_size);
createTrackbar( "Plate Thr", name_ctrl, &threshold2_val, 255, NULL, NULL);
//createTrackbar( "PThr size", name_ctrl, &athr2_size, 255, athr_callback, &athr2_size);
waitKey();
for(;;) {
//try {
cap >> frame;
//} catch(int e) {
// frame = imread(argv[1], 3);
//}
cvtColor(frame, bwframe, CV_RGB2GRAY);
imshow(name_bwframe, bwframe);
// extract V channel from HSV image
Mat vframe(frame.rows, frame.cols, CV_8UC1);
Mat tvframe(frame.rows, frame.cols, CV_8UC3);
cvtColor(frame, tvframe, CV_RGB2HSV);
int from_to[] = { 2,0 };
mixChannels( &tvframe, 1, &vframe,1, from_to, 1);
// maximize contrast
// https://github.com/oesmith/OpenANPR/blob/master/anpr/preprocess.py
Mat el=getStructuringElement(MORPH_ELLIPSE, Size(3,3), Point(1,1));
Mat bh(vframe.rows, vframe.cols, CV_8UC1);
Mat th(vframe.rows, vframe.cols, CV_8UC1);
Mat s1(vframe.rows, vframe.cols, CV_8UC1);
morphologyEx(vframe, th, MORPH_TOPHAT, el, Point(1,1), 1);
morphologyEx(vframe, bh, MORPH_BLACKHAT, el, Point(1,1), 1);
add(vframe, th, s1);
subtract(s1,bh, vframe);
// Smooth image to remove rumor
GaussianBlur(vframe, vframe, Size(5,5), 5, 5, BORDER_DEFAULT);
imshow("AAA", vframe);
// apply your filter
Canny(bwframe, edge, threshold_val, 255);
//Canny(vframe, edge, threshold_val, 255);
//threshold( bwframe, edge, threshold_val, 255, CV_THRESH_BINARY );
//threshold( vframe, edge, threshold_val, 255, CV_THRESH_BINARY );
//adaptiveThreshold(bwframe, edge, threshold_val,
// CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, athr_size, 5);
// OR CV_ADAPTIVE_THRES_MEAN_C
//adaptiveThreshold(vframe, edge, threshold_val, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY_INV, athr_size, 9);
// find the contours
vector< vector<Point> > contours;
findContours(edge, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
vector<double> areas(contours.size());
for( i = 0; i < contours.size(); i++) {
areas[i] = fabs(contourArea(Mat(contours[i])));
if(areas[i] >= minimum_area && areas[i] <= maximum_area) {
vector<Point> results;
approxPolyDP(Mat(contours[i]), results, arcLength(Mat(contours[i]),1)*perimeter_constant,1);
if (results.size() == 4 && isContourConvex(results)){
// you could also reuse bwframe here
Mat mask = Mat::zeros(bwframe.rows, bwframe.cols, CV_8UC1);
// CV_FILLED fills the connected components found
drawContours(mask, contours, i, Scalar(255,255,255), CV_FILLED);
// draw contours to cover plate external lines
drawContours(bwframe, contours, i, Scalar(255,255,255), 2, 2);
Rect box = boundingRect(Mat(contours[i]));
// let's create a new image now
Mat crop(frame.rows, frame.cols, CV_8UC1);
// set background color to black
//crop.setTo(Scalar(0,0,0));
crop.setTo(Scalar(0));
// and copy the magic apple
bwframe.copyTo(crop, mask);
// normalize so imwrite(...)/imshow(...) shows the mask correctly!
normalize(mask.clone(), mask, 0.0, 255.0, CV_MINMAX, CV_8UC1);
//rectangle(crop, box, Scalar(0,0,255) ,3);
Mat roi(crop, box);
// Image for OCR
Mat ocrimg(roi.rows, roi.cols, CV_8UC1);
ocrimg.setTo(Scalar(255));
roi.copyTo(ocrimg, roi);
int scalefactor = 1000/ocrimg.cols;
//cout << "Scale factor: " << scalefactor << endl;;
resize(ocrimg, ocrimg, Size(0,0), scalefactor, scalefactor, CV_INTER_CUBIC);
threshold(ocrimg, ocrimg, threshold2_val, 255, CV_THRESH_BINARY );
//adaptiveThreshold(ocrimg, ocrimg, threshold2_val,
// CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, athr2_size, 5);
//Canny(ocrimg, ocrimg, threshold2_val, 255);
//#if defined(__DMDEBUG)
imshow(name_crop, ocrimg);
//#endif
rectangle(frame, box, Scalar(0,0,255), 3);
rectangle(bwframe, box, Scalar(0,0,255), 3);
#if defined(__DMDEBUG)
imshow(name_mask, mask);
#endif
//OCR->TesseractRect(roi.data, 1, roi.step1(), 0,0,roi.cols, roi.rows);
OCR->SetImage((uchar*)ocrimg.data, ocrimg.size().width, ocrimg.size().height,
ocrimg.channels(), ocrimg.step1());
OCR->Recognize(0);
char* detected_text = OCR->GetUTF8Text();
//cout << "Size text: " << strlen(detected_text) << endl;
if(strlen(detected_text) > 0) {
cout << "License plate number: " << detected_text << endl;
//#if !defined(__DMDEBUG)
//imshow(name_crop, ocrimg);
//#endif
imshow(name_main, frame);
imshow(name_bwframe, bwframe);
imshow(name_edge, edge);
imshow(name_ctrl, ocrimg);
#if defined(__DMDEBUG)
imwrite("antani.jpg", ocrimg);
#endif
waitKey();
//#endif
}
}
}
}
imshow(name_main, frame);
imshow(name_bwframe, bwframe);
imshow(name_edge, edge);
if(waitKey(30) >= 0) break;
}
return 0;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment