You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

PlateSegmentation.cpp 10 kB

4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. //
  2. // Created by Jack Yu on 16/10/2017.
  3. //
  4. #include "../include/PlateSegmentation.h"
  5. #include "../include/niBlackThreshold.h"
  6. namespace pr {
  7. PlateSegmentation::PlateSegmentation(std::string prototxt,
  8. std::string caffemodel) {
  9. net = cv::dnn::readNetFromCaffe(prototxt, caffemodel);
  10. }
  11. cv::Mat PlateSegmentation::classifyResponse(const cv::Mat &cropped) {
  12. cv::Mat inputBlob = cv::dnn::blobFromImage(
  13. cropped, 1 / 255.0, cv::Size(22, 22), cv::Scalar(0, 0, 0), false);
  14. net.setInput(inputBlob, "data");
  15. return net.forward();
  16. }
  17. void drawHist(float *seq, int size, const char *name) {
  18. cv::Mat image(300, size, CV_8U);
  19. image.setTo(0);
  20. float *start = seq;
  21. float *end = seq + size;
  22. float l = *std::max_element(start, end);
  23. for (int i = 0; i < size; i++) {
  24. int p = int(float(seq[i]) / l * 300);
  25. cv::line(image, cv::Point(i, 300), cv::Point(i, 300 - p),
  26. cv::Scalar(255, 255, 255));
  27. }
  28. cv::resize(image, image, cv::Size(600, 100));
  29. cv::imshow(name, image);
  30. }
  31. inline void computeSafeMargin(int &val, const int &rows) {
  32. val = std::min(val, rows);
  33. val = std::max(val, 0);
  34. }
  35. cv::Rect boxFromCenter(const cv::Point center, int left, int right, int top,
  36. int bottom, cv::Size bdSize) {
  37. cv::Point p1(center.x - left, center.y - top);
  38. cv::Point p2(center.x + right, center.y + bottom);
  39. p1.x = std::max(0, p1.x);
  40. p1.y = std::max(0, p1.y);
  41. p2.x = std::min(p2.x, bdSize.width - 1);
  42. p2.y = std::min(p2.y, bdSize.height - 1);
  43. cv::Rect rect(p1, p2);
  44. return rect;
  45. }
  46. cv::Rect boxPadding(cv::Rect rect, int left, int right, int top, int bottom,
  47. cv::Size bdSize) {
  48. cv::Point center(rect.x + (rect.width >> 1), rect.y + (rect.height >> 1));
  49. int rebuildLeft = (rect.width >> 1) + left;
  50. int rebuildRight = (rect.width >> 1) + right;
  51. int rebuildTop = (rect.height >> 1) + top;
  52. int rebuildBottom = (rect.height >> 1) + bottom;
  53. return boxFromCenter(center, rebuildLeft, rebuildRight, rebuildTop,
  54. rebuildBottom, bdSize);
  55. }
  56. void PlateSegmentation::refineRegion(cv::Mat &plateImage,
  57. const std::vector<int> &candidatePts,
  58. const int padding,
  59. std::vector<cv::Rect> &rects) {
  60. int w = candidatePts[5] - candidatePts[4];
  61. int cols = plateImage.cols;
  62. int rows = plateImage.rows;
  63. for (int i = 0; i < candidatePts.size(); i++) {
  64. int left = 0;
  65. int right = 0;
  66. if (i == 0) {
  67. left = candidatePts[i];
  68. right = left + w + padding;
  69. } else {
  70. left = candidatePts[i] - padding;
  71. right = left + w + padding * 2;
  72. }
  73. computeSafeMargin(right, cols);
  74. computeSafeMargin(left, cols);
  75. cv::Rect roi(left, 0, right - left, rows - 1);
  76. cv::Mat roiImage;
  77. plateImage(roi).copyTo(roiImage);
  78. if (i >= 1) {
  79. cv::Mat roi_thres;
  80. // cv::threshold(roiImage,roi_thres,0,255,cv::THRESH_OTSU|cv::THRESH_BINARY);
  81. niBlackThreshold(roiImage, roi_thres, 255, cv::THRESH_BINARY, 15, 0.27,
  82. BINARIZATION_NIBLACK);
  83. std::vector<std::vector<cv::Point>> contours;
  84. cv::findContours(roi_thres, contours, cv::RETR_LIST,
  85. cv::CHAIN_APPROX_SIMPLE);
  86. cv::Point boxCenter(roiImage.cols >> 1, roiImage.rows >> 1);
  87. cv::Rect final_bdbox;
  88. cv::Point final_center;
  89. int final_dist = INT_MAX;
  90. for (auto contour : contours) {
  91. cv::Rect bdbox = cv::boundingRect(contour);
  92. cv::Point center(bdbox.x + (bdbox.width >> 1),
  93. bdbox.y + (bdbox.height >> 1));
  94. int dist = (center.x - boxCenter.x) * (center.x - boxCenter.x);
  95. if (dist < final_dist and bdbox.height > rows >> 1) {
  96. final_dist = dist;
  97. final_center = center;
  98. final_bdbox = bdbox;
  99. }
  100. }
  101. // rebuild box
  102. if (final_bdbox.height / static_cast<float>(final_bdbox.width) > 3.5 &&
  103. final_bdbox.width * final_bdbox.height < 10)
  104. final_bdbox = boxFromCenter(final_center, 8, 8, (rows >> 1) - 3,
  105. (rows >> 1) - 2, roiImage.size());
  106. else {
  107. if (i == candidatePts.size() - 1)
  108. final_bdbox = boxPadding(final_bdbox, padding / 2, padding,
  109. padding / 2, padding / 2, roiImage.size());
  110. else
  111. final_bdbox = boxPadding(final_bdbox, padding, padding, padding,
  112. padding, roiImage.size());
  113. // std::cout<<final_bdbox<<std::endl;
  114. // std::cout<<roiImage.size()<<std::endl;
  115. #ifdef DEBUG
  116. cv::imshow("char_thres", roi_thres);
  117. cv::imshow("char", roiImage(final_bdbox));
  118. cv::waitKey(0);
  119. #endif
  120. }
  121. final_bdbox.x += left;
  122. rects.push_back(final_bdbox);
  123. //
  124. } else {
  125. rects.push_back(roi);
  126. }
  127. // else
  128. // {
  129. //
  130. // }
  131. // cv::GaussianBlur(roiImage,roiImage,cv::Size(7,7),3);
  132. //
  133. // cv::imshow("image",roiImage);
  134. // cv::waitKey(0);
  135. }
  136. }
  137. void avgfilter(float *angle_list, int size, int windowsSize) {
  138. float *filterd = new float[size];
  139. for (int i = 0; i < size; i++)
  140. filterd[i] = angle_list[i];
  141. // memcpy(filterd,angle_list,size);
  142. cv::Mat kernal_gaussian = cv::getGaussianKernel(windowsSize, 3, CV_32F);
  143. float *kernal = (float *)kernal_gaussian.data;
  144. // kernal+=windowsSize;
  145. int r = windowsSize / 2;
  146. for (int i = 0; i < size; i++) {
  147. float avg = 0.00f;
  148. for (int j = 0; j < windowsSize; j++) {
  149. if (i + j - r > 0 && i + j + r < size - 1)
  150. avg += filterd[i + j - r] * kernal[j];
  151. }
  152. // avg = avg / windowsSize;
  153. angle_list[i] = avg;
  154. }
  155. delete filterd;
  156. }
  157. void PlateSegmentation::templateMatchFinding(
  158. const cv::Mat &respones, int windowsWidth,
  159. std::pair<float, std::vector<int>> &candidatePts) {
  160. int rows = respones.rows;
  161. int cols = respones.cols;
  162. float *data = (float *)respones.data;
  163. float *engNum_prob = data;
  164. float *false_prob = data + cols;
  165. float *ch_prob = data + cols * 2;
  166. avgfilter(engNum_prob, cols, 5);
  167. avgfilter(false_prob, cols, 5);
  168. std::vector<int> candidate_pts(7);
  169. int cp_list[7];
  170. float loss_selected = -10;
  171. for (int start = 0; start < 20; start += 2)
  172. for (int width = windowsWidth - 5; width < windowsWidth + 5; width++) {
  173. for (int interval = windowsWidth / 2; interval < windowsWidth;
  174. interval++) {
  175. int cp1_ch = start;
  176. int cp2_p0 = cp1_ch + width;
  177. int cp3_p1 = cp2_p0 + width + interval;
  178. int cp4_p2 = cp3_p1 + width;
  179. int cp5_p3 = cp4_p2 + width + 1;
  180. int cp6_p4 = cp5_p3 + width + 2;
  181. int cp7_p5 = cp6_p4 + width + 2;
  182. int md1 = (cp1_ch + cp2_p0) >> 1;
  183. int md2 = (cp2_p0 + cp3_p1) >> 1;
  184. int md3 = (cp3_p1 + cp4_p2) >> 1;
  185. int md4 = (cp4_p2 + cp5_p3) >> 1;
  186. int md5 = (cp5_p3 + cp6_p4) >> 1;
  187. int md6 = (cp6_p4 + cp7_p5) >> 1;
  188. if (cp7_p5 >= cols)
  189. continue;
  190. float loss =
  191. ch_prob[cp1_ch] * 3 -
  192. (false_prob[cp3_p1] + false_prob[cp4_p2] + false_prob[cp5_p3] +
  193. false_prob[cp6_p4] + false_prob[cp7_p5]);
  194. if (loss > loss_selected) {
  195. loss_selected = loss;
  196. cp_list[0] = cp1_ch;
  197. cp_list[1] = cp2_p0;
  198. cp_list[2] = cp3_p1;
  199. cp_list[3] = cp4_p2;
  200. cp_list[4] = cp5_p3;
  201. cp_list[5] = cp6_p4;
  202. cp_list[6] = cp7_p5;
  203. }
  204. }
  205. }
  206. candidate_pts[0] = cp_list[0];
  207. candidate_pts[1] = cp_list[1];
  208. candidate_pts[2] = cp_list[2];
  209. candidate_pts[3] = cp_list[3];
  210. candidate_pts[4] = cp_list[4];
  211. candidate_pts[5] = cp_list[5];
  212. candidate_pts[6] = cp_list[6];
  213. candidatePts.first = loss_selected;
  214. candidatePts.second = candidate_pts;
  215. };
  216. void PlateSegmentation::segmentPlateBySlidingWindows(cv::Mat &plateImage,
  217. int windowsWidth,
  218. int stride,
  219. cv::Mat &respones) {
  220. cv::Mat plateImageGray;
  221. cv::cvtColor(plateImage, plateImageGray, cv::COLOR_BGR2GRAY);
  222. int padding = plateImage.cols - 136;
  223. int height = plateImage.rows - 1;
  224. int width = plateImage.cols - 1 - padding;
  225. for (int i = 0; i < width - windowsWidth + 1; i += stride) {
  226. cv::Rect roi(i, 0, windowsWidth, height);
  227. cv::Mat roiImage = plateImageGray(roi);
  228. cv::Mat response = classifyResponse(roiImage);
  229. respones.push_back(response);
  230. }
  231. respones = respones.t();
  232. }
  233. void PlateSegmentation::segmentPlatePipline(PlateInfo &plateInfo, int stride,
  234. std::vector<cv::Rect> &Char_rects) {
  235. cv::Mat plateImage = plateInfo.getPlateImage(); // get src image .
  236. cv::Mat plateImageGray;
  237. cv::cvtColor(plateImage, plateImageGray, cv::COLOR_BGR2GRAY);
  238. // do binarzation
  239. std::pair<float, std::vector<int>> sections; // segment points variables .
  240. cv::Mat respones; // three response of every sub region from origin image .
  241. segmentPlateBySlidingWindows(plateImage, DEFAULT_WIDTH, 1, respones);
  242. templateMatchFinding(respones, DEFAULT_WIDTH / stride, sections);
  243. for (int i = 0; i < sections.second.size(); i++) {
  244. sections.second[i] *= stride;
  245. }
  246. refineRegion(plateImageGray, sections.second, 5, Char_rects);
  247. }
  248. void PlateSegmentation::ExtractRegions(PlateInfo &plateInfo,
  249. std::vector<cv::Rect> &rects) {
  250. cv::Mat plateImage = plateInfo.getPlateImage();
  251. for (int i = 0; i < rects.size(); i++) {
  252. cv::Mat charImage;
  253. plateImage(rects[i]).copyTo(charImage);
  254. if (charImage.channels())
  255. cv::cvtColor(charImage, charImage, cv::COLOR_BGR2GRAY);
  256. cv::equalizeHist(charImage, charImage);
  257. std::pair<CharType, cv::Mat> char_instance;
  258. if (i == 0) {
  259. char_instance.first = CHINESE;
  260. } else if (i == 1) {
  261. char_instance.first = LETTER;
  262. } else {
  263. char_instance.first = LETTER_NUMS;
  264. }
  265. char_instance.second = charImage;
  266. plateInfo.appendPlateChar(char_instance);
  267. }
  268. }
  269. } // namespace pr