2014-10-09 15 views
6

Mam problem z segmentacją następujących obrazów tablic rejestracyjnych, podczas progowania następujących po nich znaków są podzielone na więcej niż 1 znak. Tak więc otrzymuję nieprawidłowy wynik OCR. Mam stosowane morfologiczną operację zamknięcia po ustawieniu wartości odcięcia obraz, nawet po tym, że nie jestem w stanie segmencie Właściwie znaków ..Segmentowanie znaków z obrazu

License Plate image 1

License Plate image 2

License Plate image 3

License Plate image 4

kod służący do segmentowania powyższych obrazów jest podany poniżej:

#include <iostream> 
#include<cv.h> 
#include<highgui.h> 

using namespace std; 
using namespace cv; 
int main(int argc, char *argv[]) 
{ 
    IplImage *img1 = cvLoadImage(argv[1] , 0); 
    IplImage *img2 = cvCloneImage(img1); 

    cvNamedWindow("Orig"); 
    cvShowImage("Orig",img1); 
    cvWaitKey(0); 

    int wind = img1->height; 
    if (wind % 2 == 0) wind += 1; 

    cvAdaptiveThreshold(img1, img1, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, 
         CV_THRESH_BINARY_INV, wind); 

    IplImage* temp = cvCloneImage(img1); 

    cvNamedWindow("Thre"); 
    cvShowImage("Thre",img1); 
    cvWaitKey(0); 

    IplConvKernel* kernal = cvCreateStructuringElementEx(3, 3, 1, 1, 
                 CV_SHAPE_RECT,NULL); 

    cvMorphologyEx(img1, img1, temp, kernal, CV_MOP_CLOSE, 1); 

    cvNamedWindow("close"); 
    cvShowImage("close",img1); 

    cvWaitKey(0); 
} 

The obrazów wyjściowych podanych poniżej ..

U Y and 2 are not segmented properly

U, P, Y and 2 are not segmented Properly

U 3 Y 2 and 5 are not segmented properly

ktoś może stanowić dobry sposób do postaci segmentu z tych zdjęć ... ??

+0

Nie jestem pewien, czy to pomoże - http://stackoverflow.com/a/10970473/2380071 Albo to - http://stackoverflow.com/a/14372743/2380071 – LKB

+0

dylatacja i erozja nie działa dla mnie .. – Deepak

Odpowiedz

12

Chciałbym pokazać, że jest szybkim, sposobem na izolację liter/cyfr w tablicach, ponieważ faktyczna segmentacja znaków nie stanowi problemu. Gdy są to obrazy wejściowe:

enter image description hereenter image description here enter image description hereenter image description here

To jest to, co masz na końcu mojego algorytmu:

enter image description hereenter image description here enter image description hereenter image description here

Więc co omówię ta odpowiedź da ci kilka pomysłów i pomoże ci pozbyć się artefaktów obecnych na końcu twojego c proces segmentacji u użytkownika. Pamiętaj, że to podejście powinno działać tylko z tymi typami obrazów, a jeśli potrzebujesz czegoś solidniejszego, musisz dostosować niektóre rzeczy lub wymyślić zupełnie nowe sposoby robienia tych rzeczy.

  • Ze względu na drastyczne zmiany w jasności, najlepiej wykonać histogram equalization poprawić kontrast i uczynić je bardziej do siebie podobne, więc wszystkie inne techniki i parametry pracy z nimi:

enter image description hereenter image description here enter image description hereenter image description here

  • Następnie bilateral filter może być stosowany w celu wygładzenia obrazów przy jednoczesnym zachowaniu ed ges obiektów, co jest ważne dla procesu binaryzacji. Ten filtr kosztuje trochę więcej mocy przetwarzania than others.

enter image description hereenter image description here enter image description hereenter image description here

  • Po że obrazy są gotowe do binaryzowany An służy do rade:

enter image description hereenter image description here enter image description hereenter image description here

  • Wynik binaryzacji jest podobny do tego, co osiągnięto, więc wymyśliłem sposób korzystania findContours() usunięcia mniejsze i większe segmenty:

enter image description hereenter image description here enter image description hereenter image description here

  • Wynik wydaje się nieco lepszy, ale zniszczył ważne segmenty postaci na talerzu. Jednak obecnie nie stanowi to problemu, ponieważ nie martwimy się o rozpoznawanie postaci: chcemy tylko wyizolować obszar, w którym się znajdują. Następnym krokiem jest więc dalsze usuwanie segmentów, w szczególności tych, które nie są wyrównane z tą samą osią Y cyfr. Kontury, które przetrwały ten proces cięcia są:

enter image description hereenter image description here enter image description hereenter image description here

  • Jest to o wiele lepsze, i w tym momencie nowy std::vector<cv::Point> jest stworzony do przechowywania wszystkich współrzędnych pikseli potrzebne do rysowania wszystko te segmenty. Jest to konieczne, aby stworzyć cv::RotatedRect co jest, co pozwala nam stworzyć bounding box a także crop the image:

enter image description hereenter image description here enter image description hereenter image description here

Od tego momentu można używać Przycięte obrazy, aby wykonać swoje własne techniki i łatwo segmentować znaki płyty.

Oto kod C++:

#include <iostream> 
#include <vector>  
#include <opencv2/highgui/highgui.hpp> 
#include <opencv2/imgproc/imgproc.hpp> 
#include <opencv2/imgproc/imgproc_c.h> 

/* The code has an outter loop where every iteration processes one of the four input images */ 

std::string files[] = { "plate1.jpg", "plate2.jpg", "plate3.jpg", "plate4.jpg" }; 
cv::Mat imgs[4]; 
for (int a = 0; a < 4; a++) 
{ 
    /* Load input image */ 

    imgs[a] = cv::imread(files[a]); 
    if (imgs[a].empty()) 
    { 
     std::cout << "!!! Failed to open image: " << imgs[a] << std::endl; 
     return -1; 
    } 

    /* Convert to grayscale */ 

    cv::Mat gray; 
    cv::cvtColor(imgs[a], gray, cv::COLOR_BGR2GRAY); 

    /* Histogram equalization improves the contrast between dark/bright areas */ 

    cv::Mat equalized; 
    cv::equalizeHist(gray, equalized); 
    cv::imwrite(std::string("eq_" + std::to_string(a) + ".jpg"), equalized); 
    cv::imshow("Hist. Eq.", equalized); 

    /* Bilateral filter helps to improve the segmentation process */ 

    cv::Mat blur; 
    cv::bilateralFilter(equalized, blur, 9, 75, 75); 
    cv::imwrite(std::string("filter_" + std::to_string(a) + ".jpg"), blur); 
    cv::imshow("Filter", blur); 

    /* Threshold to binarize the image */ 

    cv::Mat thres; 
    cv::adaptiveThreshold(blur, thres, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, 15, 2); //15, 2 
    cv::imwrite(std::string("thres_" + std::to_string(a) + ".jpg"), thres); 
    cv::imshow("Threshold", thres); 

    /* Remove small segments and the extremelly large ones as well */ 

    std::vector<std::vector<cv::Point> > contours; 
    cv::findContours(thres, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE); 

    double min_area = 50; 
    double max_area = 2000; 
    std::vector<std::vector<cv::Point> > good_contours; 
    for (size_t i = 0; i < contours.size(); i++) 
    { 
     double area = cv::contourArea(contours[i]); 
     if (area > min_area && area < max_area) 
      good_contours.push_back(contours[i]); 
    } 

    cv::Mat segments(gray.size(), CV_8U, cv::Scalar(255)); 
    cv::drawContours(segments, good_contours, -1, cv::Scalar(0), cv::FILLED, 4); 
    cv::imwrite(std::string("segments_" + std::to_string(a) + ".jpg"), segments); 
    cv::imshow("Segments", segments); 

    /* Examine the segments that survived the previous lame filtering process 
    * to figure out the top and bottom heights of the largest segments. 
    * This info will be used to remove segments that are not aligned with 
    * the letters/numbers of the plate. 
    * This technique is super flawed for other types of input images. 
    */ 

    // Figure out the average of the top/bottom heights of the largest segments 
    int min_average_y = 0, max_average_y = 0, count = 0; 
    for (size_t i = 0; i < good_contours.size(); i++) 
    { 
     std::vector<cv::Point> c = good_contours[i]; 
     double area = cv::contourArea(c); 
     if (area > 200) 
     { 
      int min_y = segments.rows, max_y = 0; 
      for (size_t j = 0; j < c.size(); j++) 
      { 
       if (c[j].y < min_y) 
        min_y = c[j].y; 

       if (c[j].y > max_y) 
        max_y = c[j].y; 
      } 
      min_average_y += min_y; 
      max_average_y += max_y; 
      count++; 
     } 
    } 
    min_average_y /= count; 
    max_average_y /= count; 
    //std::cout << "Average min: " << min_average_y << " max: " << max_average_y << std::endl; 

    // Create a new vector of contours with just the ones that fall within the min/max Y 
    std::vector<std::vector<cv::Point> > final_contours; 
    for (size_t i = 0; i < good_contours.size(); i++) 
    { 
     std::vector<cv::Point> c = good_contours[i]; 
     int min_y = segments.rows, max_y = 0; 
     for (size_t j = 0; j < c.size(); j++) 
     { 
      if (c[j].y < min_y) 
       min_y = c[j].y; 

      if (c[j].y > max_y) 
       max_y = c[j].y; 
     } 

     // 5 is to add a little tolerance from the average Y coordinate 
     if (min_y >= (min_average_y-5) && (max_y <= max_average_y+5)) 
      final_contours.push_back(c); 
    } 

    cv::Mat final(gray.size(), CV_8U, cv::Scalar(255)); 
    cv::drawContours(final, final_contours, -1, cv::Scalar(0), cv::FILLED, 4); 
    cv::imwrite(std::string("final_" + std::to_string(a) + ".jpg"), final); 
    cv::imshow("Final", final); 


    // Create a single vector with all the points that make the segments 
    std::vector<cv::Point> points; 
    for (size_t x = 0; x < final_contours.size(); x++) 
    { 
     std::vector<cv::Point> c = final_contours[x]; 
     for (size_t y = 0; y < c.size(); y++) 
      points.push_back(c[y]); 
    } 

    // Compute a single bounding box for the points 
    cv::RotatedRect box = cv::minAreaRect(cv::Mat(points)); 
    cv::Rect roi; 
    roi.x = box.center.x - (box.size.width/2); 
    roi.y = box.center.y - (box.size.height/2); 
    roi.width = box.size.width; 
    roi.height = box.size.height; 

    // Draw the box at on equalized image 
    cv::Point2f vertices[4]; 
    box.points(vertices); 
    for(int i = 0; i < 4; ++i) 
     cv::line(imgs[a], vertices[i], vertices[(i + 1) % 4], cv::Scalar(255, 0, 0), 1, CV_AA); 
    cv::imwrite(std::string("box_" + std::to_string(a) + ".jpg"), imgs[a]); 
    cv::imshow("Box", imgs[a]); 

    // Crop the equalized image with the area defined by the ROI 
    cv::Mat crop = equalized(roi); 
    cv::imwrite(std::string("crop_" + std::to_string(a) + ".jpg"), crop); 
    cv::imshow("crop", crop); 

    /* The cropped image should contain only the plate's letters and numbers. 
    * From here on you can use your own techniques to segment the characters properly. 
    */ 

    cv::waitKey(0); 
} 

Dla pełniejszego i solidnego sposobu prowadzenia rozpoznawanie tablic rejestracyjnych z OpenCV, spojrzeć na Mastering OpenCV with Practical Computer Vision Projects, rozdział 5. Source code is available on Github!

+0

Jak możemy segmentować segmenty z ostatecznego obrazu? (operacja dylatacji i zamykania morfologicznego nie działa idealnie, ponieważ w większości tablic rejestracyjnych znaki są bardzo blisko siebie) – Deepak

+0

W ostatecznym obrazie, który podałeś, znaki Y, 5, 2 są rozłączone.Używając cvFindContour(), otrzymamy je jako różne segmenty, co spowoduje błędne wyjście OCR. – Deepak

+0

[Możesz] (https://github.com/aperrau/detecttext) [wykonaj] (http://stackoverflow.com/a/12785869/176769) [wiele różnych] (http://stackoverflow.com/q/23104244/176769) [podejścia] (http://docs.opencv.org/trunk/modules/text/doc/text.html) [do tego] (http://stackoverflow.com/q/23361583/176769) , w tym rozdział wspomniany powyżej. – karlphillip