// 阈值分割
vector<CharSegment> OCR::segment(Plate plate) {
    Mat input = plate.plateImg;
    vector<CharSegment> output;
    Mat thresholdImage;
    threshold(input, thresholdImage, 60, 255, CV_THRESH_BINARY_INV);
    if (DEBUG)
        imshow("Threshold plate", thresholdImage);
    Mat img_contours;
    thresholdImage.copyTo(img_contours);
    // 找到可能的车牌的轮廓
    vector< vector< Point> > contours;
    findContours(img_contours,
                 contours, // 检测的轮廓数组,每一个轮廓用一个point类型的vector表示
                 CV_RETR_EXTERNAL, // 表示只检测外轮廓
                 CV_CHAIN_APPROX_NONE); // 轮廓的近似办法,这里存储所有的轮廓点

    // 在白色的图上画出蓝色的轮廓
    cv::Mat result;

    thresholdImage.copyTo(result);
    cvtColor(result, result, CV_GRAY2RGB);
    cv::drawContours(result, contours,
                     -1,  // 所有的轮廓都画出
                     cv::Scalar(255, 0, 0), // 颜色
                     1); // 线粗

    // 对每个轮廓检测和提取最小区域的有界矩形区域
    vector<vector<Point> >::iterator itc = contours.begin();

    char res[20];
    int i = 0;
    // 若没有达到设定的宽高比要求,移去该区域
    while (itc != contours.end())
    {
        Rect mr = boundingRect(Mat(*itc));
        rectangle(result, mr, Scalar(0, 255, 0));
        // 裁剪图像
        Mat auxRoi(thresholdImage, mr);
        if (verifySizes(auxRoi)) {
            auxRoi = preprocessChar(auxRoi);
            output.push_back(CharSegment(auxRoi, mr));
            //保存每个字符图片
            sprintf(res, "PlateNumber%d.jpg", i);
            i++;
            imwrite(res, auxRoi);
            rectangle(result, mr, Scalar(0, 125, 255));
        }
        ++itc;
    }
    if (DEBUG)
        cout << "Num chars: " << output.size() << "\n";

    if (DEBUG)
        imshow("SEgmented Chars", result);
    return output;
}
void	DetectRegions::part2( const cv::Mat& input,
			      std::vector<img_Plate>& output,
			      cv::Mat& img_threshold,
			      const std::string& out_id )
{

  cv::Mat	my_input;
  input.copyTo(my_input);

  //Find contours of possibles plates
  std::vector< std::vector< cv::Point> > contours;
  findContours( img_threshold,
  		contours, // a vector of contours
  		CV_RETR_EXTERNAL, // retrieve the external contours
  		// CV_CHAIN_APPROX_NONE ); // all pixels of each contours
		CV_CHAIN_APPROX_SIMPLE );


  //Start to iterate to each contour founded
  std::vector< std::vector<cv::Point> >::iterator itc = contours.begin();
  std::vector<cv::RotatedRect> rects;



  cv::Mat	my_input_rect;
  input.copyTo(my_input_rect);

  //Remove patch that are no inside limits of aspect ratio and area.
  while (itc != contours.end())
    {

      //Create bounding rect of object
      cv::RotatedRect	mr = minAreaRect(cv::Mat(*itc));

      if (!verifySizes(mr))
	{
	  itc = contours.erase(itc);

	  // rotated rectangle drawing
	  cv::Point2f	rect_points[4];
	  mr.points( rect_points );
	  for (int j = 0; j < 4; ++j)
	    line( my_input_rect, rect_points[j], rect_points[ (j + 1) % 4 ],
		  cv::Scalar(255,0,0), 1, 8 );

	}
      else
  	{
  	  ++itc;
  	  rects.push_back(mr);

	  // rotated rectangle drawing
	  cv::Point2f	rect_points[4];
	  mr.points( rect_points );
	  for (int j = 0; j < 4; ++j)
	    line( my_input_rect, rect_points[j], rect_points[ (j + 1) % 4 ],
		  cv::Scalar(0,255,0), 2, 8 );

  	}

    }


  D_IMG_SAVE( my_input_rect, "img_" << out_id << "Rect.png" );


  // Draw blue contours on a white image
  cv::Mat result;
  input.copyTo(result);
  cv::drawContours( result, contours,
		    -1, // draw all contours
		    cv::Scalar(255,0,0), // in blue
		    1 ); // with a thickness of 1
		    // 3 ); // with a thickness of 1

  D_IMG_SAVE( result, "04_img_" << out_id << "Contours.png" );


  // std::cerr << "rects.size : " << rects.size() << std::endl;

  std::vector<cv::Mat>	Mats;

  for (unsigned int i = 0; i < rects.size(); ++i)
    {


      //For better rect cropping for each posible box
      //Make floodfill algorithm because the plate has white background
      //And then we can retrieve more clearly the contour box
      circle(result, rects[i].center, 3, cv::Scalar(0,255,0), -1);

      //get the min size between width and height
      // float minSize = ( (rects[i].size.width < rects[i].size.height)
      float	minSize = ( (rects[i].size.width > rects[i].size.height)
			    ? (rects[i].size.width)
			    : (rects[i].size.height) );
      minSize = minSize - minSize * 0.5;

      //initialize rand and get 5 points around center for floodfill algorithm
      srand ( time(NULL) );

      //Initialize floodfill parameters and variables
      cv::Mat mask;
      mask.create(input.rows + 2, input.cols + 2, CV_8UC1);
      mask = cv::Scalar::all(0);
      int loDiff = 30;
      int upDiff = 30;
      int connectivity = 4;
      int newMaskVal = 255;
      // int NumSeeds = 100;
      cv::Rect ccomp;
      int flags = connectivity + (newMaskVal << 8) + CV_FLOODFILL_FIXED_RANGE + CV_FLOODFILL_MASK_ONLY;


      int	max_size = rects[i].size.width * rects[i].size.height;


      cv::Rect	b_rect = rects[i].boundingRect();

      int	min_x = b_rect.x;
      int	min_y = b_rect.y;

      int	max_x = min_x + b_rect.width;
      int	max_y = min_y + b_rect.height;

      for (int local_y = min_y; local_y < max_y; local_y += 5)
	for (int local_x = min_x; local_x < max_x; local_x += 5)
	  {
	    cv::Point	seed;

	    seed.x = local_x;
	    seed.y = local_y;

	    if (Collision( contours[i], seed ))
	      {
		cv::Mat	tmp_mask;
		tmp_mask.create( input.rows + 2, input.cols + 2, CV_8UC1 );
		tmp_mask = cv::Scalar::all(0);

		int	area = floodFill( input, tmp_mask, seed,
	  				  cv::Scalar(255,0,0), &ccomp,
	  				  cv::Scalar(loDiff, loDiff, loDiff),
	  				  cv::Scalar(upDiff, upDiff, upDiff), flags );



		{
		  cv::Point	c( ccomp.x + ccomp.width / 2,
			   ccomp.y + ccomp.height / 2 );

		  cv::Size	s( ccomp.width, ccomp.height );

		  cv::RotatedRect	tmp_rect( c, s, 0 );

		  // rotated rectangle drawing
		  cv::Point2f	rect_points[4];
		  tmp_rect.points( rect_points );
		  for (int j = 0; j < 4; ++j)
		    line( my_input, rect_points[j], rect_points[ (j + 1) % 4 ],
			  cv::Scalar(0,255,255), 1, 8 );
		}


		bool	rect_invalid = ( ccomp.x < min_x || ccomp.x > max_x ||
					 ccomp.y < min_y || ccomp.y > max_y );


		cv::Point	left_top( min_x, min_y );
		cv::Point	right_top( max_x, min_y );

		cv::Point	left_bottom( min_x, max_y );
		cv::Point	right_bottom( max_x, max_y );

		if (area > max_size)
		  {
		    circle( result, seed, 1, cv::Scalar(255,0,0), -1 );
		    circle( my_input, seed, 1, cv::Scalar(255,0,0), -1 );
		  }

		else if (rect_invalid)
		  {
		    circle( result, seed, 1, cv::Scalar(255,0,0), -1 );
		    circle( my_input, seed, 1, cv::Scalar(255,0,0), -1 );
		  }

		else
		  {
		    circle( result, seed, 1, cv::Scalar(0,255,0), -1 );
		    circle( my_input, seed, 1, cv::Scalar(0,255,0), -1 );

		    floodFill( input, mask, seed,
			       cv::Scalar(255,0,0), &ccomp,
			       cv::Scalar(loDiff, loDiff, loDiff),
			       cv::Scalar(upDiff, upDiff, upDiff), flags );
		  }

	      }
	    else
	      {
		circle( result, seed, 1, cv::Scalar(255,0,0), -1 );
		circle( my_input, seed, 1, cv::Scalar(255,0,0), -1 );
	      }

	  } // for (int j = 0; j < NumSeeds; ++j)


      {


	// rotated rectangle drawing
	cv::Point2f	rect_points[4];
	rects[i].points( rect_points );
	for (int j = 0; j < 4; ++j)
	  line( my_input, rect_points[j], rect_points[ (j + 1) % 4 ],
		cv::Scalar(255,255,255), 2, 8 );


	D_IMG_SAVE( mask, "img_" << out_id << "" << i << "_01_Mask.png" );

      }

      //cvWaitKey(0);

      //Check new floodfill mask match for a correct patch.
      //Get all points detected for get Minimal rotated Rect
      std::vector<cv::Point>	pointsInterest;
      cv::Mat_<uchar>::iterator	itMask = mask.begin<uchar>();
      cv::Mat_<uchar>::iterator	end = mask.end<uchar>();
      for (; itMask != end; ++itMask)
	if (*itMask == 255)
	  pointsInterest.push_back(itMask.pos());

      if (pointsInterest.size() < 2)
	continue;

      cv::RotatedRect	minRect = minAreaRect(pointsInterest);

      if (verifySizes(minRect))
	{

	  // rotated rectangle drawing
	  cv::Point2f rect_points[4]; minRect.points( rect_points );
	  for( int j = 0; j < 4; j++ )
	    line( result, rect_points[j], rect_points[(j+1)%4], cv::Scalar(0,0,255), 1, 8 );

	  //Get rotation matrix
	  float r = (float)minRect.size.width / (float)minRect.size.height;
	  float angle=minRect.angle;

	  if (r < 1)
	    angle = 90 + angle;

	  cv::Mat rotmat = getRotationMatrix2D(minRect.center, angle,1);

	  //Create and rotate image
	  cv::Mat img_rotated;
	  warpAffine(input, img_rotated, rotmat, input.size(), CV_INTER_CUBIC);

	  //Crop image
	  cv::Size rect_size = minRect.size;

	  if (r < 1)
	    std::swap( rect_size.width, rect_size.height );

	  cv::Mat img_crop;
	  getRectSubPix(img_rotated, rect_size, minRect.center, img_crop);


	  D_IMG_SAVE( img_crop, "img_" << out_id << "" << i << "_02_crop.png" );

	  cv::Mat	resultResized;
	  resultResized.create(33,144, CV_8UC3);
	  resize(img_crop, resultResized, resultResized.size(), 0, 0, cv::INTER_CUBIC);


	  D_IMG_SAVE( resultResized, "img_" << out_id << "" << i << "_03_resultResized.png" );

	  output.push_back( img_Plate( resultResized, minRect.boundingRect() ) );

	  // //Equalize croped image
	  // cv::Mat grayResult;
	  // cvtColor(resultResized, grayResult, CV_BGR2GRAY);
	  // // blur(grayResult, grayResult, Size(3,3));
	  // grayResult = histeq(grayResult);


	  // D_IMG_SAVE( grayResult, "img_" << out_id << "" << i << "_04_grayResult.png" );

	  // output.push_back( Plate( grayResult, minRect.boundingRect() ) );

	} // if (verifySizes(minRect))

    } // for (int i = 0; i < rects.size(); ++i)

  D_IMG_SAVE( result, "10_img_" << out_id << "Contours.png" );

  D_IMG_SAVE( my_input, "11_img_" << out_id << "my_input.png" );
}
//! 字符分割与排序
int CCharsSegment::charsSegment(Mat input, vector<Mat>& resultVec)
{
	if( !input.data )
	{ return -3; }

	//判断车牌颜色以此确认threshold方法
	int plateType = getPlateType(input);
	cvtColor(input, input, CV_RGB2GRAY);

	//Threshold input image
	Mat img_threshold;
	if (1 == plateType)
		threshold(input, img_threshold, 10, 255, CV_THRESH_OTSU+CV_THRESH_BINARY);
	else 
		threshold(input, img_threshold, 10, 255, CV_THRESH_OTSU+CV_THRESH_BINARY_INV);

	if(m_debug)
	{ 
		stringstream ss(stringstream::in | stringstream::out);
		ss << "image/tmp/debug_char_threshold" << ".jpg";
		imwrite(ss.str(), img_threshold);
	}

	//去除车牌上方的柳钉以及下方的横线等干扰
	clearLiuDing(img_threshold);


	if(m_debug)
	{ 
		stringstream ss(stringstream::in | stringstream::out);
		ss << "image/tmp/debug_char_clearLiuDing" << ".jpg";
		imwrite(ss.str(), img_threshold);
	}

	Mat img_contours;
	img_threshold.copyTo(img_contours);

	vector< vector< Point> > contours;
	findContours(img_contours,
		contours, // a vector of contours
		CV_RETR_EXTERNAL, // retrieve the external contours
		CV_CHAIN_APPROX_NONE); // all pixels of each contours

	//Start to iterate to each contour founded
	vector<vector<Point> >::iterator itc= contours.begin();

	vector<Rect> vecRect;

	//Remove patch that are no inside limits of aspect ratio and area.  
	//将不符合特定尺寸的图块排除出去
	while (itc != contours.end()) 
	{
		Rect mr = boundingRect(Mat(*itc));
		Mat auxRoi(img_threshold, mr);
		if (verifySizes(auxRoi))
		{
			vecRect.push_back(mr);
		}
		++itc;
	}
	
	if (vecRect.size() == 0)
		return -3;

	vector<Rect> sortedRect;
	//对符合尺寸的图块按照从左到右进行排序
	SortRect(vecRect, sortedRect);

	int specIndex = 0;
	//获得指示城市的特定Rect,如苏A的"A"
	specIndex = GetSpecificRect(sortedRect);

	if(m_debug)
	{ 
		if (specIndex < sortedRect.size())
		{
			Mat specMat(img_threshold, sortedRect[specIndex]);
			stringstream ss(stringstream::in | stringstream::out);
			ss << "image/tmp/debug_specMat" <<".jpg";
			imwrite(ss.str(), specMat);
		}
	}

	//根据特定Rect向左反推出中文字符
	//这样做的主要原因是根据findContours方法很难捕捉到中文字符的准确Rect,因此仅能
	//退过特定算法来指定
	Rect chineseRect;
	if (specIndex < sortedRect.size())
		chineseRect = GetChineseRect(sortedRect[specIndex]);
	else
		return -3;

	if(m_debug)
	{ 
		Mat chineseMat(img_threshold, chineseRect);
		stringstream ss(stringstream::in | stringstream::out);
		ss << "image/tmp/debug_chineseMat" <<".jpg";
		imwrite(ss.str(), chineseMat);
	}


	//新建一个全新的排序Rect
	//将中文字符Rect第一个加进来,因为它肯定是最左边的
	//其余的Rect只按照顺序去6个,车牌只可能是7个字符!这样可以避免阴影导致的“1”字符
	vector<Rect> newSortedRect;
	newSortedRect.push_back(chineseRect);
	RebuildRect(sortedRect, newSortedRect, specIndex);

	if (newSortedRect.size() == 0)
		return -3;

	for (int i = 0; i < newSortedRect.size(); i++)
	{
		Rect mr = newSortedRect[i];
		Mat auxRoi(img_threshold, mr);

		if (1)
		{
			auxRoi = preprocessChar(auxRoi);
			if(m_debug)
			{ 
				stringstream ss(stringstream::in | stringstream::out);
				ss << "image/tmp/debug_char_auxRoi_" << i <<".jpg";
				imwrite(ss.str(), auxRoi);
			}
			resultVec.push_back(auxRoi);
		}
	}

	return 0;
}
Beispiel #4
0
//! 定位车牌图像
//! src 原始图像
//! resultVec 一个Mat的向量,存储所有抓取到的图像
//! 成功返回0,否则返回-1
int CPlateLocate::plateLocate(Mat src, vector<Mat>& resultVec)
{
	Mat src_blur, src_gray;
	Mat grad;

	int scale = SOBEL_SCALE;
	int delta = SOBEL_DELTA;
	int ddepth = SOBEL_DDEPTH;

	if( !src.data )
	{ return -1; }

	//高斯均衡。Size中的数字影响车牌定位的效果。
	GaussianBlur( src, src_blur, Size(m_GaussianBlurSize, m_GaussianBlurSize), 
		0, 0, BORDER_DEFAULT );

	/// Convert it to gray
	cvtColor( src_blur, src_gray, CV_RGB2GRAY );

	/// Generate grad_x and grad_y
	Mat grad_x, grad_y;
	Mat abs_grad_x, abs_grad_y;

	/// Gradient X
	//Scharr( src_gray, grad_x, ddepth, 1, 0, scale, delta, BORDER_DEFAULT );
	Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT );
	convertScaleAbs( grad_x, abs_grad_x );

	/// Gradient Y
	//Scharr( src_gray, grad_y, ddepth, 0, 1, scale, delta, BORDER_DEFAULT );
	Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT );
	convertScaleAbs( grad_y, abs_grad_y );

	/// Total Gradient (approximate)
	addWeighted( abs_grad_x, SOBEL_X_WEIGHT, abs_grad_y, SOBEL_Y_WEIGHT, 0, grad );

	Mat img_threshold;
	threshold(grad, img_threshold, 0, 255, CV_THRESH_OTSU+CV_THRESH_BINARY);
	//threshold(grad, img_threshold, 75, 255, CV_THRESH_BINARY);

	Mat element = getStructuringElement(MORPH_RECT, Size(m_MorphSizeWidth, m_MorphSizeHeight) );
	morphologyEx(img_threshold, img_threshold, CV_MOP_CLOSE, element);
	
	//Find 轮廓 of possibles plates
	vector< vector< Point> > contours;
	findContours(img_threshold,
		contours, // a vector of contours
		CV_RETR_EXTERNAL, // 提取外部轮廓
		CV_CHAIN_APPROX_NONE); // all pixels of each contours

	//Start to iterate to each contour founded
	vector<vector<Point> >::iterator itc = contours.begin();
	
	vector<RotatedRect> rects;
	//Remove patch that are no inside limits of aspect ratio and area.
	int t = 0;
	while (itc != contours.end())
	{
		//Create bounding rect of object
		RotatedRect mr = minAreaRect(Mat(*itc));

		//large the rect for more
		if( !verifySizes(mr))
		{
			itc = contours.erase(itc);
		}
		else
		{
			++itc;
			rects.push_back(mr);
		}
	}

	for(int i=0; i< rects.size(); i++)
	{
		RotatedRect minRect = rects[i];
		if(verifySizes(minRect))
		{	
			// rotated rectangle drawing 
			// Get rotation matrix
			// 旋转这部分代码确实可以将某些倾斜的车牌调整正,
			// 但是它也会误将更多正的车牌搞成倾斜!所以综合考虑,还是不使用这段代码。
			// 2014-08-14,由于新到的一批图片中发现有很多车牌是倾斜的,因此决定再次尝试
			// 这段代码。
			float r = (float)minRect.size.width / (float)minRect.size.height;
			float angle = minRect.angle;
			Size rect_size = minRect.size;
			if (r < 1)
			{
				angle = 90 + angle;
				swap(rect_size.width, rect_size.height);
			}
			//如果抓取的方块旋转超过m_angle角度,则不是车牌,放弃处理
			if (angle - m_angle < 0 && angle + m_angle > 0)
			{
				//Create and rotate image
				Mat rotmat = getRotationMatrix2D(minRect.center, angle, 1);
				Mat img_rotated;
				warpAffine(src, img_rotated, rotmat, src.size(), CV_INTER_CUBIC);

				Mat resultMat;
				resultMat = showResultMat(img_rotated, rect_size, minRect.center);

				resultVec.push_back(resultMat);
			}
		}
	}
	return 0;
}
void RegionDetector::getMask()
{
    //find contours
    vector <vector<Point> > contours;
    cv::findContours(img_temp,contours,
                     CV_RETR_EXTERNAL,	//retrive external contours
                     CV_CHAIN_APPROX_NONE);	// all pixels of each contour
    //extract rectangle of minimal area
    //Start to iterate to each contour found
    vector <vector<Point> >::iterator itc = contours.begin();
    vector <RotatedRect> rects;
    //Remove patch that has no inside limits of aspect ratio and area
    while (itc!=contours.end())
    {
        //create bounding rect of object
        RotatedRect rr = minAreaRect(Mat(*itc));
        if (!verifySizes(rr)) {
            itc = contours.erase(itc);
        } else {
            std::cout<<"bene";
            itc++;
            rects.push_back(rr);
        }
    }
    std::cout << rects.size();
    // Draw blue contours on a white image
    Mat result;
    img_temp.copyTo(result);
    cv::drawContours(result,contours,
                     -1,  // draw all contours
                     cv::Scalar(255),// in blue
                     1); // thickness
    imshow("nn",result);
    for(int i=0; i< rects.size(); i++)
    {
        std::cout<<"true\n";
        //For better rect cropping for each posible box
        //Make floodfill algorithm because the plate has white background
        //And then we can retrieve more clearly the contour box
        circle(result, rects[i].center, 3, Scalar(0,255,0), -1);
        //get the min size between width and height
        float minSize=(rects[i].size.width < rects[i].size.height)?rects[i].size.width:rects[i].size.height;
        minSize=minSize-minSize*0.5;
        //initialize rand and get 5 points around center for floodfill algorithm
        srand ( time(NULL) );
        //Initialize floodfill parameters and variables
        Mat mask;
        mask.create(img_temp.rows + 2, img_temp.cols + 2, CV_8UC1);
        mask= Scalar::all(0);
        int loDiff = 30;
        int upDiff = 30;
        int connectivity = 4;
        int newMaskVal = 255;
        int NumSeeds = 10;
        Rect ccomp;
        int flags = connectivity + (newMaskVal << 8 ) + CV_FLOODFILL_FIXED_RANGE + CV_FLOODFILL_MASK_ONLY;
        for(int j=0; j<NumSeeds; j++)
        {
            Point seed;
            seed.x=rects[i].center.x+rand()%(int)minSize-(minSize/2);
            seed.y=rects[i].center.y+rand()%(int)minSize-(minSize/2);
            circle(result, seed, 1, Scalar(0,255,255), -1); //
            int area = floodFill(img_temp, mask, seed, Scalar(255,0,0), &ccomp, Scalar(loDiff, loDiff, loDiff), Scalar(upDiff, upDiff, upDiff), flags);
        }
        imshow("MASK", mask);

    }


}
Beispiel #6
0
vector<Plate> DetectRegions::segment(Mat input){
    vector<Plate> output;

    //convert image to gray
    Mat img_gray; //= *new Mat(input.size().width,input.size().height, CV_8UC1);
    cvtColor(input, img_gray, CV_BGR2GRAY);
    blur(img_gray, img_gray, Size(5,5));

    //Finde vertical lines. Car plates have high density of vertical lines
    Mat img_sobel;
    Sobel(img_gray, img_sobel, CV_8U, 1, 0, 3, 1, 0, BORDER_DEFAULT);
    if(showSteps)
        imshow("Sobel", img_sobel);

    //threshold image
    Mat img_threshold;
    threshold(img_sobel, img_threshold, 0, 255, CV_THRESH_OTSU+CV_THRESH_BINARY);
    if(showSteps)
        imshow("Threshold", img_threshold);

    //Morphplogic operation close
    Mat element = getStructuringElement(MORPH_RECT, Size(17, 3) );
    morphologyEx(img_threshold, img_threshold, CV_MOP_CLOSE, element);
    if(showSteps)
        imshow("Close", img_threshold);

    //Find contours of possibles plates
    vector< vector< Point> > contours;
    findContours(img_threshold,
            contours, // a vector of contours
            CV_RETR_EXTERNAL, // retrieve the external contours
            CV_CHAIN_APPROX_NONE); // all pixels of each contours

    //Start to iterate to each contour founded
    vector<vector<Point> >::iterator itc= contours.begin();
    vector<RotatedRect> rects;

    //Remove patch that are no inside limits of aspect ratio and area.    
    while (itc!=contours.end()) {
        //Create bounding rect of object
        RotatedRect mr= minAreaRect(Mat(*itc));
        if( !verifySizes(mr)){
            itc= contours.erase(itc);
        }else{
            ++itc;
            rects.push_back(mr);
        }
    }

    // Draw blue contours on a white image
    cv::Mat result;
    input.copyTo(result);
    cv::drawContours(result,contours,
            -1, // draw all contours
            cv::Scalar(255,0,0), // in blue
            1); // with a thickness of 1

    for(int i=0; i< rects.size(); i++){

        //For better rect cropping for each posible box
        //Make floodfill algorithm because the plate has white background
        //And then we can retrieve more clearly the contour box
        circle(result, rects[i].center, 3, Scalar(0,255,0), -1);
        //get the min size between width and height
        float minSize=(rects[i].size.width < rects[i].size.height)?rects[i].size.width:rects[i].size.height;
        minSize=minSize-minSize*0.5;
        //initialize rand and get 5 points around center for floodfill algorithm
        srand ( time(NULL) );
        //Initialize floodfill parameters and variables
        Mat mask;
        mask.create(input.rows + 2, input.cols + 2, CV_8UC1);
        mask= Scalar::all(0);
        int loDiff = 30;
        int upDiff = 30;
        int connectivity = 4;
        int newMaskVal = 255;
        int NumSeeds = 10;
        Rect ccomp;
        int flags = connectivity + (newMaskVal << 8 ) + CV_FLOODFILL_FIXED_RANGE + CV_FLOODFILL_MASK_ONLY;
        for(int j=0; j<NumSeeds; j++){
            Point seed;
            seed.x=rects[i].center.x+rand()%(int)minSize-(minSize/2);
            seed.y=rects[i].center.y+rand()%(int)minSize-(minSize/2);
            circle(result, seed, 1, Scalar(0,255,255), -1);
            int area = floodFill(input, mask, seed, Scalar(255,0,0), &ccomp, Scalar(loDiff, loDiff, loDiff), Scalar(upDiff, upDiff, upDiff), flags);
        }
        if(showSteps)
            imshow("MASK", mask);
        //cvWaitKey(0);

        //Check new floodfill mask match for a correct patch.
        //Get all points detected for get Minimal rotated Rect
        vector<Point> pointsInterest;
        Mat_<uchar>::iterator itMask= mask.begin<uchar>();
        Mat_<uchar>::iterator end= mask.end<uchar>();
        for( ; itMask!=end; ++itMask)
            if(*itMask==255)
                pointsInterest.push_back(itMask.pos());

        RotatedRect minRect = minAreaRect(pointsInterest);

        if(verifySizes(minRect)){
            // rotated rectangle drawing 
            Point2f rect_points[4]; minRect.points( rect_points );
            for( int j = 0; j < 4; j++ )
                line( result, rect_points[j], rect_points[(j+1)%4], Scalar(0,0,255), 1, 8 );    

            //Get rotation matrix
            float r= (float)minRect.size.width / (float)minRect.size.height;
            float angle=minRect.angle;    
            if(r<1)
                angle=90+angle;
            Mat rotmat= getRotationMatrix2D(minRect.center, angle,1);

            //Create and rotate image
            Mat img_rotated;
            warpAffine(input, img_rotated, rotmat, input.size(), CV_INTER_CUBIC);

            //Crop image
            Size rect_size=minRect.size;
            if(r < 1)
                swap(rect_size.width, rect_size.height);
            Mat img_crop;
            getRectSubPix(img_rotated, rect_size, minRect.center, img_crop);
            
            Mat resultResized;
            resultResized.create(33,144, CV_8UC3);
            resize(img_crop, resultResized, resultResized.size(), 0, 0, INTER_CUBIC);
            //Equalize croped image
            Mat grayResult;
            cvtColor(resultResized, grayResult, CV_BGR2GRAY); 
            blur(grayResult, grayResult, Size(3,3));
            grayResult=histeq(grayResult);
            if(saveRegions){ 
                stringstream ss(stringstream::in | stringstream::out);
                ss << "tmp/" << filename << "_" << i << ".jpg";
                imwrite(ss.str(), grayResult);
            }
            output.push_back(Plate(grayResult,minRect.boundingRect()));
        }
    }       
    if(showSteps) 
        imshow("Contours", result);

    return output;
}
Beispiel #7
0
//Segment the chars from plate
vector<CharSegment> OCR::segment(Plate plate){
    Mat input=plate.plateImg;
    vector<CharSegment> output;

    //Threshold input image
    Mat img_threshold;
    //To make char image clearly
//    threshold(input, img_threshold, 60, 255, CV_THRESH_BINARY_INV);	//Spain
//    threshold(input, img_threshold, 150~160, 255, CV_THRESH_BINARY);	//China
    // TODO: IMPORTANT
    threshold(input, img_threshold, 175, 255, CV_THRESH_BINARY);	//China
    if(debug) {
        imshow("OCR_Threshold_Binary", img_threshold);
    }

    Mat img_contours;
    img_threshold.copyTo(img_contours);
    //Find contours of possibles characters
    vector< vector< Point> > contours;
    findContours(img_contours,
            contours, // a vector of contours
            CV_RETR_EXTERNAL, // retrieve the external contours
            CV_CHAIN_APPROX_NONE); // all pixels of each contours
    
    // Draw blue contours on a white image
    cv::Mat result;
    img_threshold.copyTo(result);
    cvtColor(result, result, CV_GRAY2RGB);
    cv::drawContours(result,
    		contours,
            -1, // draw all contours
            cv::Scalar(255,0,0), // in BLUE
            1); // with a thickness of 1

    //Start to iterate to each contour founded
    vector<vector<Point> >::iterator itc = contours.begin();
    //Remove patch that are no inside limits of aspect ratio and area.    
    while (itc!=contours.end()) {
        //Create bounding rect of object
        Rect mr = boundingRect(Mat(*itc));
        rectangle(result, mr, Scalar(0,255,0));	//Possible chars in GREEN

        //Crop image
        Mat auxRoi(img_threshold, mr);
        if(verifySizes(auxRoi)){
            auxRoi=preprocessChar(auxRoi);
            output.push_back(CharSegment(auxRoi, mr));
            rectangle(result, mr, Scalar(0,0,255));	//Possible chars in RED
        }
        ++itc;
    }

    if(debug)
    {
        cout << "OCR number of chars: " << output.size() << "\n";
        imshow("OCR Chars", result);
        cvWaitKey(0);
    }

    return output;
}