I have made some changes to an image such as histogram, dilation, gray scale, edge detection etc. to identify the number plate in an image.The below code is part of where i slice each charterer in the number plate and storing it in Mat. Now I'd like to use tesseract to identify each character. but instead tesseract throw this warning. " Warning. Invalid resolution 0 dpi. Using 70 instead." and OCR output is empty. Can anyone help me.
Note: for now i'm doing only for one pic. just to check whether is it working or not.
if (!Plate.empty())
{
imshow("Final Plate", Plate);
waitKey();
int x = OTSU(Plate);
Mat BlurBinary = ConvertToBinary(Plate, x);
Mat BinPlate = BlurBinary.clone();
imshow("BinaryBlurImg", BlurBinary);
waitKey();
vector<vector<Point>>contours2;
vector<Vec4i>hierachy2;
findContours(BlurBinary, contours2, hierachy2, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point(0, 0));
Mat dst1 = Mat::zeros(BlurBinary.size(), CV_8UC3);
if (!contours1.empty()) {
int i = 0;
for (int i = 0; i < contours2.size(); i++) {
Scalar color((rand() & 255), (rand() & 255), (rand() & 255));
drawContours(dst1, contours2, i, color, CV_FILLED, 8, hierachy2);
}
}
Rect BlobRect2;
for (int i = 0; i < contours2.size(); i++) {
BlobRect2 = boundingRect(contours2[i]);
if (BlobRect2.height < 10)
{
drawContours(BinPlate, contours2, i, black, CV_FILLED, 8, hierachy2);
}
else
{
Mat character;
character = Plate(BlobRect2);
imshow("character", character);
waitKey();
imwrite(format("C:/Users/Di/source/repos/LPRrecognition/Characters/Img%d.tif", i), character);
}
}
}
else {
cout << "no plate sorry";
}
char *outText;
tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
// Initialize tesseract-ocr with English, without specifying tessdata path
if (api->Init(NULL, "eng")) {
fprintf(stderr, "Could not initialize tesseract.\n");
exit(1);
}
// Open input image with leptonica library
Pix *image1 = pixRead("C:\\Users\\Di\\source\\repos\\LPRrecognition\\Characters\\Img1.tif");
api->SetImage(image1);
// Get OCR result
outText = api->GetUTF8Text();
printf("OCR output:\n%s", outText);
cout << outText;
// Destroy used object and release memory
api->End();
delete[] outText;
pixDestroy(&image1);
Related
I need to get contour from hand image, usually I process image with 4 steps:
get raw RGB gray image from 3 channels to 1 channel:
cvtColor(sourceGrayImage, sourceGrayImage, COLOR_BGR2GRAY);
use Gaussian blur to filter gray image:
GaussianBlur(sourceGrayImage, sourceGrayImage, Size(3,3), 0);
binary gray image, I split image by height, normally I split image to 6 images by its height, then each one I do threshold process:
// we split source picture to binaryImageSectionCount(here it's 8) pieces by its height,
// then we for every piece, we do threshold,
// and at last we combine them agin to binaryImage
const binaryImageSectionCount = 8;
void GetBinaryImage(Mat &grayImage, Mat &binaryImage)
{
// get every partial gray image's height
int partImageHeight = grayImage.rows / binaryImageSectionCount;
for (int i = 0; i < binaryImageSectionCount; i++)
{
Mat partialGrayImage;
Mat partialBinaryImage;
Rect partialRect;
if (i != binaryImageSectionCount - 1)
{
// if it's not last piece, Rect's height should be partImageHeight
partialRect = Rect(0, i * partImageHeight, grayImage.cols, partImageHeight);
}
else
{
// if it's last piece, Rect's height should be (grayImage.rows - i * partImageHeight)
partialRect = Rect(0, i * partImageHeight, grayImage.cols, grayImage.rows - i * partImageHeight);
}
Mat partialResource = grayImage(partialRect);
partialResource.copyTo(partialGrayImage);
threshold( partialGrayImage, partialBinaryImage, 0, 255, THRESH_OTSU);
// combin partial binary image to one piece
partialBinaryImage.copyTo(binaryImage(partialRect));
///*stringstream resultStrm;
//resultStrm << "partial_" << (i + 1);
//string string = resultStrm.str();
//imshow(string, partialBinaryImage);
//waitKey(0);*/
}
imshow("result binary image.", binaryImage);
waitKey(0);
return;
}
use findcontour to get biggest area contour:
vector<vector<Point> > contours;
findContours(binaryImage, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
normally it works well,
But for some low quality gray image, it doesn't work,like below:
the complete code is here:
#include <opencv2/imgproc/imgproc.hpp>
#include<opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
// we split source picture to binaryImageSectionCount(here it's 8) pieces by its height,
// then we for every piece, we do threshold,
// and at last we combine them agin to binaryImage
const binaryImageSectionCount = 8;
void GetBinaryImage(Mat &grayImage, Mat &binaryImage)
{
// get every partial gray image's height
int partImageHeight = grayImage.rows / binaryImageSectionCount;
for (int i = 0; i < binaryImageSectionCount; i++)
{
Mat partialGrayImage;
Mat partialBinaryImage;
Rect partialRect;
if (i != binaryImageSectionCount - 1)
{
// if it's not last piece, Rect's height should be partImageHeight
partialRect = Rect(0, i * partImageHeight, grayImage.cols, partImageHeight);
}
else
{
// if it's last piece, Rect's height should be (grayImage.rows - i * partImageHeight)
partialRect = Rect(0, i * partImageHeight, grayImage.cols, grayImage.rows - i * partImageHeight);
}
Mat partialResource = grayImage(partialRect);
partialResource.copyTo(partialGrayImage);
threshold( partialGrayImage, partialBinaryImage, 0, 255, THRESH_OTSU);
// combin partial binary image to one piece
partialBinaryImage.copyTo(binaryImage(partialRect));
///*stringstream resultStrm;
//resultStrm << "partial_" << (i + 1);
//string string = resultStrm.str();
//imshow(string, partialBinaryImage);
//waitKey(0);*/
}
imshow("result binary image.", binaryImage);
waitKey(0);
return;
}
int main(int argc, _TCHAR* argv[])
{
// get image path
string imgPath("C:\\Users\\Alfred\\Desktop\\gray.bmp");
// read image
Mat src = imread(imgPath);
imshow("Source", src);
//medianBlur(src, src, 7);
cvtColor(src, src, COLOR_BGR2GRAY);
imshow("gray", src);
// do filter
GaussianBlur(src, src, Size(3,3), 0);
// binary image
Mat threshold_output(src.rows, src.cols, CV_8UC1, Scalar(0, 0, 0));
GetBinaryImage(src, threshold_output);
imshow("binaryImage", threshold_output);
// get biggest contour
vector<vector<Point> > contours;
findContours(threshold_output,contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
int biggestContourIndex = 0;
int maxContourArea = -1000;
for (int i = 0; i < contours.size(); i++)
{
if (contourArea(contours[i]) > maxContourArea)
{
maxContourArea = contourArea(contours[i]);
biggestContourIndex = i;
}
}
// show biggest contour
Mat biggestContour(threshold_output.rows, threshold_output.cols, CV_8UC1, Scalar(0, 0, 0));
drawContours(biggestContour, contours, biggestContourIndex, cv::Scalar(255,255,255), 2, 8, vector<Vec4i>(), 0, Point());
imshow("maxContour", biggestContour);
waitKey(0);
}
could anybody please help me to get a better hand contour result?
thanks!!!
I have the code snippet in python, you can follow the same approach in C:
img = cv2.imread(x, 1)
cv2.imshow("img",img)
imgray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.imshow("gray",imgray)
#Code for histogram equalization
equ = cv2.equalizeHist(imgray)
cv2.imshow('equ', equ)
#Code for contrast limited adaptive histogram equalization
#clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8))
#cl2 = clahe.apply(imgray)
#cv2.imshow('clahe2', cl2)
This is the result I obtained:
If you're image is horribly bad you could try the code that I commented involving contrast limited adaptive histogram equalization.
friends, could you please help with my questions?
I am using opencv in c++.
I am randomly cropping a small picture from a camera view. I want to find the word located at the bottom of this cropped picture, and this word should also be penetrated by the vertical center line (imaginary) of this cropped picture. please see the following code :
char* my_word = do_ocr(my_cropped_image);
and the do_ocr function is like this:
char* do_ocr(cv::Mat im)
{
cv::Mat gray;
cv::cvtColor(im, gray, CV_BGR2GRAY);
// ...other image pre-processing here...
// Pass it to Tesseract API
tesseract::TessBaseAPI tess;
tess.Init(NULL, "eng", tesseract::OEM_DEFAULT);
tess.SetPageSegMode(tesseract::PSM_SINGLE_BLOCK);
tess.SetImage((uchar*)gray.data, gray.cols, gray.rows, 1, gray.cols);
// Get the text
char* out = tess.GetUTF8Text();
std::cout << out << std::endl;
return out;
}
The following is the schematic diagram and some samples of my_cropped_image :
my_cropped_image sample # 1, the letter "preceding" should be detected:
my_cropped_image sample # 2, the letter "advantageous" should be detected:
my_cropped_image sample # 3, the letter "Correlation" should be detected:
my_cropped_image sample # 4, the letter "density" should be detected:
my_cropped_image sample # 5, the letter "time" should be detected:
I'll appreciate the helps from you to update my do_ocr function.
Thank you and have a great day!
Are these the results you were looking for?
Methodology:
1) Binaryze the image, white is foreground. Here is simply done with img = img < 150;. You can use more sophisticated methods, like adaptiveThreshold.
You get something like:
2) Apply a open morphological operation, so that all the letters in a single word for a single blob:
3) Find the rectangle of each connected component:
4) Take the bottom one, in the center.
Here the full code:
#include <opencv2\opencv.hpp>
#include <vector>
using namespace std;
using namespace cv;
Mat3b dbg;
int main()
{
Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);
cvtColor(img, dbg, COLOR_GRAY2BGR);
Mat3b result;
cvtColor(img, result, COLOR_GRAY2BGR);
Mat1b img2;
img2 = img < 150;
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(5,3));
morphologyEx(img2, img2, MORPH_DILATE, kernel);
// Apply a small border
copyMakeBorder(img2, img2, 5, 5, 5, 5, BORDER_CONSTANT, Scalar(0));
// Take the bounding boxes of all connected components
vector<vector<Point>> contours;
findContours(img2.clone(), contours, CV_RETR_LIST, CHAIN_APPROX_NONE);
int minArea = 60;
vector<Rect> rects;
for (int i = 0; i < contours.size(); ++i)
{
Rect r = boundingRect(contours[i]);
if (r.area() >= minArea)
{
// Account for border
r -= Point(5,5);
rects.push_back(r);
}
}
int middle = img.cols / 2;
// Keep bottom rect, containig middle point
if (rects.empty()) return -1;
Rect word;
for (int i = 1; i < rects.size(); ++i)
{
Point pt(middle, rects[i].y + rects[i].height/2);
if (rects[i].contains(pt))
{
if (rects[i].y > word.y)
{
word = rects[i];
}
}
}
// Show results
Mat3b res;
cvtColor(img, res, COLOR_GRAY2BGR);
for (int i = 0; i < rects.size(); ++i)
{
rectangle(res, rects[i], Scalar(0, 255, 0));
}
rectangle(result, word, Scalar(0, 0, 255), 2);
imshow("Rects", res);
imshow("Result", result);
waitKey();
return 0;
}
I am trying to calculate HOG features on GPU for different levels and then I am saving features of each level to a yml file. Below is the function that I am using.
void App::run()
{
unsigned int count = 0;
FileStorage fs;
running = true;
int width = 640;
int height = 480;
Size win_size(args.win_width, args.win_width * 2);
Size win_stride(args.win_stride_width, args.win_stride_height);
cv::gpu::HOGDescriptor gpu_hog(win_size, Size(16, 16), Size(8, 8), Size(8, 8), 9,
cv::gpu::HOGDescriptor::DEFAULT_WIN_SIGMA, 0.2, gamma_corr,
cv::gpu::HOGDescriptor::DEFAULT_NLEVELS);
VideoCapture vc("/home/ubuntu/Desktop/getdescriptor/images/image%d.jpg");
Mat frame;
Mat Left;
Mat img_aux, img, img_to_show, img_new;
cv::Mat temp;
gpu::GpuMat gpu_img, descriptors, new_img;
char cbuff[20];
while (running)
{
vc.read(frame);
if (!frame.empty())
{
workBegin();
sprintf (cbuff, "%04d", count);
// Change format of the image
if (make_gray) cvtColor(frame, img_aux, CV_BGR2GRAY);
else if (use_gpu) cvtColor(frame, img_aux, CV_BGR2BGRA);
else Left.copyTo(img_aux);
// Resize image
if (args.resize_src) resize(img_aux, img, Size(args.width, args.height));
else img = img_aux;
img_to_show = img;
gpu_hog.nlevels = nlevels;
hogWorkBegin();
if (use_gpu)
{
gpu_img.upload(img);
new_img.upload(img_new);
fs.open(cbuff, FileStorage::WRITE);
//double scale = 1.05;
for(int levels = 0; levels < nlevels; levels++)
{
gpu_hog.getDescriptors(gpu_img, win_stride, descriptors, cv::gpu::HOGDescriptor::DESCR_FORMAT_ROW_BY_ROW);
descriptors.download(temp);
printf("size %d %d\n", temp.rows, temp.cols);
fs <<"level" << levels;
fs << "features" << temp;
cout<<"("<<width<<","<<height<<")"<<endl;
width = round(width/scale);
height = round(height/scale);
cout<<"Levels "<<levels<<endl;
if(width < win_size.width || height < win_size.height)
break;
resize(img,img_new,Size(width,height));
scale *= scale;
}
cout<<count<<endl;
count++;
}
hogWorkEnd();
fs.release();
}
else running = false;
}
}
For the first image it is correctly calculating HOG features for all levels but for the next image it takes the old value of width and height and in that case it break the following loop.
if(width < win_size.width || height < win_size.height)
break;
Can some one point my mistake. I tried to debug but unfortunately no success yet.
HOG feature calculation of each image takes old value of the following three parameters.
1. Width
2. Height
3. Scale
And when it calculates HOG features for next image it break the loop immediately. A common programming mistake.
I am working with face detection.I need to compare current and previous(successive) faces.Can any one help me how to take current and previous faces from a continuous face detection.I am not getting any idea about this.
This is my face detection code
void facedetect(IplImage* image)
{
ct1++;
cvNamedWindow("output");
int j=0,i,count=0,strsize;
char numstr[50];
CvPoint ul,lr,w,h,ul1,lr1;
CvRect *r;
string path;
IplImage* image1;IplImage* tmpsize;IplImage* reimg;
CvHaarClassifierCascade* cascade=(CvHaarClassifierCascade*) cvLoad(cascade_name);
CvMemStorage* storage=cvCreateMemStorage(0);
const char *extract;
if(!cascade)
{
cout<<"Coulid not load classifier cascade"<<endl;
}
if(cascade)
{ faces=cvHaarDetectObjects(image,cascade,storage,1.1,1,CV_HAAR_DO_CANNY_PRUNING,cvSize(10,10));
for(int i=0;i<(faces ? faces->total : 0);i++)
{
string s1="im",re,rename,ex=".pgm";
sprintf(numstr, "%d", k);
re = s1 + numstr;
rename=re+ex;
char *extract1=new char[rename.size()+1];
extract1[rename.size()]=0; memcpy(extract1,rename.c_str(),rename.size());
strsize=rename.size();
r=(CvRect*) cvGetSeqElem(faces,i);
ul.x=r->x;
ul.y=r->y;
w.x=r->width;
h.y=r->height;
lr.x=(r->x + r->width);
lr.y=(r->y + r->height);
cvSetImageROI(image,cvRect(ul.x,ul.y,w.x,h.y));
image1=cvCreateImage(cvGetSize(image),image->depth,image->nChannels);
cvCopy(image, image1, NULL);
reimg=resizeImage(image1, 40, 40, true);
saveImage(reimg,extract1);
Mat img=cvarrToMat(reimg);
//Mat img1=cvarrToMat();
imshow("result",img); //this is the current image, but i need to get the previous image also.
//readImag(img,"/home/athira/Image/folderr", path);
cvResetImageROI(image);
cvRectangle(image,ul,lr,CV_RGB(1,255,0),3,8,0);
j++,count++;
k++;
arr[l]=ul.x;
arr1[l]=ul.y;
cout<<"frame"<<ct1<<" "<<"face"<<ct<<":"<<"x: "<<ul.x<<endl;
cout<<"frame"<<ct1<<" "<<"face"<<ct<<":"<<"y: "<<ul.y<<endl;
cout<<""<<endl;
ct++;
//compareImages(l,faces->total);
//feature_extract(img,img1);
l++;
}
width=image->width;
height=image->height;
resoltion=width*height;
s=(float) 0.0301/100;
diff=s*resoltion;
cvShowImage("output",image);
cvWaitKey(0);
}
}
When i use the canny edge algorithm, it produces the 2 edges opposite the thick colored line as expected, but i want only one edge to be displayed so as to make my line and curve detection algorithm much less complicated, any ideas on how i can make that happen ?
Here is the code :
bool CannyEdgeDetection(DataStructure& col)
{
Mat src, src_gray;
Mat dst, detected_edges, fin;
int WhiteCount = 0, BCount = 0;
char szFil1[32] = "ocv.bmp";
char szFil2[32] = "dst.bmp";
src = imread(szFil1);
dst = imread(szFil1);
blur( src_gray, detected_edges, Size(3,3) );
Canny( src, dst, 100, 200, 3 );
imwrite(szFil2, dst );
IplImage* img = cvLoadImage(szFil2);
int height = img->height;
int width = img->width;
int step = img->widthStep;
int channels = img->nChannels;
uchar * datau = (uchar *)img->imageData;
for(int i=0;i<height;i++){
for(int j=0;j<width;j++){
for(int k=0;k<channels;k++){
datau[i*step+j*channels+k] = 255 - datau[i*step+j*channels+k];
if (datau[i*step+j*channels+k]==0){
WhiteCount++;
col.pixel_col [i][j] = 2;
}
else{BCount++;
col.pixel_col[i][j] = 0;
}
}
}
}
cvSaveImage("img.bmp" ,img);
return 0;
}
This is not the original image but similar :
Which part do i comment out to be able to read black images in white backgrounds ? or any colored image ?
bool done;
do
{
cv::morphologyEx(img, temp, cv::MORPH_OPEN, element);
cv::bitwise_not(temp, temp);
cv::bitwise_and(img, temp, temp);
cv::bitwise_or(skel, temp, skel);
cv::erode(img, img, element);
double max;
cv::minMaxLoc(img, 0, &max);
done = (max == 0);
} while (!done);
That process is called skeletonization or thinning. You can google for that.
Here is a simple method for skeletonization : skeletonization OpenCV In C#
Below is the output I got when applied above method to your image ( Image is inverted before skeletonization because above method work for white images in black background, just opposite case of your input image).