CrossDetector.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. #include "StdAfx.h"
  2. #include "CrossDetector.h"
  3. #include <math.h>
  4. #include "schema_struct.h"
  5. #define PI 3.1415926535
  6. const int DIRECTION_FLAG[4] = { DIR_FLAG_UP, DIR_FLAG_DOWN, DIR_FLAG_LEFT, DIR_FLAG_RIGHT };
  7. //检测
  8. CCrossDetector::CCrossDetector(int unit_count/*=15*/, double angle/*=0*/, double angle_unit/*=0.5*/, int cross_break/*=4*/, int cross_burr/*=15*/, int grayvalve/*=180*/, int cross_len/*=20*/, int cross_percent/*=80*/)
  9. {
  10. options.m_CrossArg = unit_count;
  11. options.m_CrossBreak = cross_break;
  12. options.m_CrossBurr = cross_burr;
  13. options.m_CrossGray = grayvalve;
  14. options.m_CrossLen = cross_len;
  15. options.m_CrossPercent = cross_percent;
  16. this->total_angle_count = (unit_count * 2 + 1);
  17. arglist_t.resize(total_angle_count);
  18. arglist = arglist_t.data();
  19. arglist[0] = static_cast<int>(angle);
  20. for (int i = 1; i <= unit_count; i++){
  21. arglist[2 * i - 1] = static_cast<int>(angle + i*angle_unit);
  22. arglist[2 * i] = static_cast<int>(angle - i*angle_unit);
  23. }
  24. buffer_t.resize(2 * 4 * total_angle_count);
  25. this->buffer = buffer_t.data();
  26. dx[0] = buffer + total_angle_count * 0;
  27. dx[1] = buffer + total_angle_count * 1;
  28. dx[2] = buffer + total_angle_count * 2;
  29. dx[3] = buffer + total_angle_count * 3;
  30. dy[0] = buffer + total_angle_count * 4;
  31. dy[1] = buffer + total_angle_count * 5;
  32. dy[2] = buffer + total_angle_count * 6;
  33. dy[3] = buffer + total_angle_count * 7;
  34. for (int i = 0; i < total_angle_count; i++)
  35. {
  36. dx[0][i] = -sin(arglist[i] * PI / 180.);
  37. dy[0][i] = -cos(arglist[i] * PI / 180.);
  38. dx[1][i] = sin(arglist[i] * PI / 180.);
  39. dy[1][i] = cos(arglist[i] * PI / 180.);
  40. dx[2][i] = -cos(arglist[i] * PI / 180.);
  41. dy[2][i] = sin(arglist[i] * PI / 180.);
  42. dx[3][i] = cos(arglist[i] * PI / 180.);
  43. dy[3][i] = -sin(arglist[i] * PI / 180.);
  44. }
  45. }
  46. int CCrossDetector::Detect(const IplImage*img, std::vector<CvCross>&cross)
  47. {
  48. IplImage t;
  49. cvInitImageHeader(&t, cvSize(img->width, img->height), img->depth, img->nChannels, img->origin, img->align);
  50. cvSetData(&t, img->imageData, img->widthStep);
  51. IplImage * img_ = &t;
  52. CvRect rect = cvGetImageROI(img);
  53. cvSetImageROI(img_,rect);
  54. IplImage * img_binary = cvCreateImage(cvSize(rect.width, rect.height), IPL_DEPTH_8U, 1);
  55. IplImage * img_handle = cvCreateImage(cvSize(rect.width, rect.height), IPL_DEPTH_8U, 1);
  56. cvZero(img_handle);
  57. if (img_->nChannels == 3){
  58. cvCvtColor(img_, img_binary, CV_BGR2GRAY);
  59. }
  60. else{
  61. cvCopy(img_, img_binary);
  62. }
  63. //cvSaveImage("D:\\001.png", img_binary);
  64. cvAdaptiveThreshold(img_binary, img_binary, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, 9, 10);
  65. cvSmooth(img_binary, img_binary, CV_BLUR,3,3);
  66. cvThreshold(img_binary, img_binary, 255*(18-3)/18, 255, CV_THRESH_BINARY);
  67. //cvSaveImage("D:\\002.png", img_binary);
  68. //vector<SIGN> signs;
  69. for (int y = 0; y < img_binary->height; y++)
  70. {
  71. for (int x = 0; x < img_binary->width; x++)
  72. {
  73. if (CV_IMAGE_ELEM(img_handle, unsigned char, y, x)){
  74. continue;
  75. }
  76. if (CV_IMAGE_ELEM(img_binary, unsigned char, y, x)){
  77. continue;
  78. }
  79. bool isSign_0 = isSignPossable(img_binary, x, y);
  80. if (!isSign_0)continue;
  81. SIGN sign;
  82. bool isSign_ = isSign(img_binary, x, y, sign);
  83. if (isSign_){
  84. int sumx = sign.x;
  85. int sumy = sign.y;
  86. int sign_count = 1;
  87. int sign_ = sign.sign;
  88. for (int yy = max(y - 5, 0), endyy = min(img_binary->height, y + 5 + 1); yy < endyy; yy++)
  89. {
  90. for (int xx = max(x - 5, 0), endxx = min(img_binary->width, x + 5 + 1); xx < endxx; xx++)
  91. {
  92. if (xx == x&&yy == y)continue;
  93. if (CV_IMAGE_ELEM(img_handle, unsigned char, yy, xx))continue;
  94. SIGN sign2;
  95. bool isSign_2 = isSign(img_binary, xx, yy, sign2);
  96. if (isSign_2){
  97. sumx += sign2.x;
  98. sumy += sign2.y;
  99. sign_ |= sign2.sign;
  100. sign_count++;
  101. CV_IMAGE_ELEM(img_handle, unsigned char, yy, xx) = 1;
  102. }
  103. }
  104. }
  105. CV_IMAGE_ELEM(img_handle, unsigned char, y, x) = 1;
  106. sign.x = rect.x + (int)(sumx / (double)sign_count + 0.5);
  107. sign.y = rect.y + (int)(sumy / (double)sign_count + 0.5);
  108. sign.sign = (SIGND)sign_;
  109. //signs.push_back(sign);
  110. CvCross cross_;
  111. cross_.x = sign.x;
  112. cross_.y = sign.y;
  113. cross_.sign = sign.sign;
  114. cross_.arg = 0;
  115. cross.push_back(cross_);
  116. }
  117. }
  118. }
  119. cvReleaseImage(&img_binary);
  120. cvReleaseImage(&img_handle);
  121. return identify::result::IDF_SUCCESS;
  122. }
  123. CCrossDetector::~CCrossDetector(void)
  124. {
  125. }
  126. bool CCrossDetector::isSign(const IplImage * img_binary, int x, int y, SIGN &sign_)
  127. {
  128. int width = img_binary->width;
  129. int height = img_binary->height;
  130. int haveburr;
  131. int totalpoints;
  132. bool result = false;
  133. if (!CV_IMAGE_ELEM(img_binary, unsigned char, y, x)){
  134. for (int arg0 = 0; arg0 < 2 * options.m_CrossArg + 1; arg0++){
  135. int nowburr = 0;
  136. int nowsign = 0;
  137. for (int dir = 0; dir < 4; dir++){
  138. int breakpoints = 0;
  139. int len;
  140. for (len = 1; len <= options.m_CrossLen; len++){
  141. int posx = x + (int)(len * dx[dir][arg0] + 0.5);
  142. int posy = y + (int)(len * dy[dir][arg0] + 0.5);
  143. if (posx < 0 || posx >= width || posy < 0 || posy >= height)break;
  144. if (CV_IMAGE_ELEM(img_binary, unsigned char, posy, posx)){ //潜在断点
  145. breakpoints++;
  146. if (breakpoints > options.m_CrossBreak){
  147. len -= breakpoints;
  148. break;
  149. }
  150. }
  151. else{
  152. breakpoints = 0;
  153. }
  154. }
  155. if (len > options.m_CrossLen){
  156. nowsign |= DIRECTION_FLAG[dir];
  157. }
  158. else if (len > options.m_CrossBurr){
  159. nowburr = 1;
  160. }
  161. }
  162. if ((nowsign & 3) && (nowsign & 12)){ //必须有交叉,不能只是直线
  163. if (options.m_CrossPercent != 0){//周围空白检查
  164. int blankpoint = 0; //交叉点周围必须有足够多的空白点
  165. int xx, yy, xx0, yy0;
  166. //*********************************右上区域检查**********************
  167. totalpoints = options.m_CrossLen / 2;
  168. //寻找直线右边界
  169. for (xx = 1; xx <= options.m_CrossLen / 2; xx++){
  170. blankpoint = 0;
  171. for (yy = -1; yy >= -options.m_CrossLen / 2; yy--){
  172. int posx = (int)(x + xx * dx[3][arg0] + 0.5);
  173. int posy = (int)(y + yy * dy[1][arg0] + 0.5);
  174. if (posx < 0 || posx >= width || posy < 0 || posy >= height)break;
  175. if (CV_IMAGE_ELEM(img_binary, unsigned char, posy, posx)) blankpoint++;
  176. }
  177. if (blankpoint > totalpoints * 0.5) break;
  178. }
  179. if (xx > options.m_CrossLen / 2) continue;
  180. xx0 = xx;
  181. //寻找直线上边界
  182. for (yy = -1; yy >= -options.m_CrossLen / 2; yy--){
  183. blankpoint = 0;
  184. for (xx = 1; xx <= options.m_CrossLen / 2; xx++){
  185. int posx = (int)(x + xx * dx[3][arg0] + 0.5);
  186. int posy = (int)(y + yy * dy[1][arg0] + 0.5);
  187. if (posx < 0 || posx >= width || posy < 0 || posy >= height)break;
  188. if (CV_IMAGE_ELEM(img_binary, unsigned char, posy, posx)) blankpoint++;
  189. }
  190. if (blankpoint > totalpoints * 0.5) break;
  191. }
  192. if (yy < -options.m_CrossLen / 2) continue;
  193. yy0 = yy;
  194. blankpoint = 0; //交叉点周围必须有足够多的空白点
  195. totalpoints = 0;
  196. for (yy = 0; yy > -options.m_CrossLen / 2; yy--){
  197. for (xx = 0; xx < options.m_CrossLen / 2 + yy; xx++){
  198. int posx = (int)(x + (xx0 + xx) * dx[3][arg0] + 0.5);
  199. int posy = (int)(y + (yy0 + yy) * dy[1][arg0] + 0.5);
  200. if (posx < 0 || posx >= width || posy < 0 || posy >= height)break;
  201. totalpoints++;
  202. if (CV_IMAGE_ELEM(img_binary, unsigned char, posy, posx)) blankpoint++;
  203. }
  204. }
  205. if (blankpoint < totalpoints / 100. * options.m_CrossPercent) continue; //空白点太少
  206. //*********************************左上区域检查**********************
  207. totalpoints = options.m_CrossLen / 2;
  208. //寻找直线左边界
  209. for (xx = -1; xx >= -options.m_CrossLen / 2; xx--){
  210. blankpoint = 0;
  211. for (yy = -1; yy >= -options.m_CrossLen / 2; yy--){
  212. int posx = (int)(x + xx * dx[3][arg0] + 0.5);
  213. int posy = (int)(y + yy * dy[1][arg0] + 0.5);
  214. if (posx < 0 || posx >= width || posy < 0 || posy >= height)break;
  215. if (CV_IMAGE_ELEM(img_binary, unsigned char, posy, posx)) blankpoint++;
  216. }
  217. if (blankpoint > totalpoints * 0.5) break;
  218. }
  219. if (xx < -options.m_CrossLen / 2) continue;
  220. xx0 = xx;
  221. //寻找直线上边界
  222. for (yy = -1; yy >= -options.m_CrossLen / 2; yy--){
  223. blankpoint = 0;
  224. for (xx = -1; xx >= -options.m_CrossLen / 2; xx--){
  225. int posx = (int)(x + xx * dx[3][arg0] + 0.5);
  226. int posy = (int)(y + yy * dy[1][arg0] + 0.5);
  227. if (posx < 0 || posx >= width || posy < 0 || posy >= height)break;
  228. if (CV_IMAGE_ELEM(img_binary, unsigned char, posy, posx)) blankpoint++;
  229. }
  230. if (blankpoint > totalpoints * 0.5) break;
  231. }
  232. if (yy < -options.m_CrossLen / 2) continue;
  233. yy0 = yy;
  234. totalpoints = 0;
  235. blankpoint = 0;
  236. for (yy = 0; yy > -options.m_CrossLen / 2; yy--){
  237. for (xx = 0; xx > -options.m_CrossLen / 2 - yy; xx--){
  238. int posx = (int)(x + (xx0 + xx) * dx[3][arg0] + 0.5);
  239. int posy = (int)(y + (yy0 + yy) * dy[1][arg0] + 0.5);
  240. if (posx < 0 || posx >= width || posy < 0 || posy >= height)break;
  241. totalpoints++;
  242. if (CV_IMAGE_ELEM(img_binary, unsigned char, posy, posx)) blankpoint++;
  243. }
  244. }
  245. if (blankpoint < totalpoints / 100. * options.m_CrossPercent) continue; //空白点太少
  246. //*********************************右下区域检查**********************
  247. totalpoints = options.m_CrossLen / 2;
  248. //寻找直线右边界
  249. for (xx = 1; xx <= options.m_CrossLen / 2; xx++){
  250. blankpoint = 0;
  251. for (yy = 1; yy <= options.m_CrossLen / 2; yy++){
  252. int posx = (int)(x + xx * dx[3][arg0] + 0.5);
  253. int posy = (int)(y + yy * dy[1][arg0] + 0.5);
  254. if (posx < 0 || posx >= width || posy < 0 || posy >= height)break;
  255. if (CV_IMAGE_ELEM(img_binary, unsigned char, posy, posx)) blankpoint++;
  256. }
  257. if (blankpoint > totalpoints * 0.5) break;
  258. }
  259. if (xx > options.m_CrossLen / 2) continue;
  260. xx0 = xx;
  261. //寻找直线下边界
  262. for (yy = 1; yy <= options.m_CrossLen / 2; yy++){
  263. blankpoint = 0;
  264. for (xx = 1; xx <= options.m_CrossLen / 2; xx++){
  265. int posx = (int)(x + xx * dx[3][arg0] + 0.5);
  266. int posy = (int)(y + yy * dy[1][arg0] + 0.5);
  267. if (posx < 0 || posx >= width || posy < 0 || posy >= height)break;
  268. if (CV_IMAGE_ELEM(img_binary, unsigned char, posy, posx)) blankpoint++;
  269. }
  270. if (blankpoint > totalpoints * 0.5) break;
  271. }
  272. if (yy > options.m_CrossLen / 2) continue;
  273. yy0 = yy;
  274. totalpoints = 0;
  275. blankpoint = 0;
  276. for (yy = 0; yy < options.m_CrossLen / 2; yy++){
  277. for (xx = 0; xx < options.m_CrossLen / 2 - yy; xx++){
  278. int posx = (int)(x + (xx0 + xx) * dx[3][arg0] + 0.5);
  279. int posy = (int)(y + (yy0 + yy) * dy[1][arg0] + 0.5);
  280. if (posx < 0 || posx >= width || posy < 0 || posy >= height)break;
  281. totalpoints++;
  282. if (CV_IMAGE_ELEM(img_binary, unsigned char, posy, posx)) blankpoint++;
  283. }
  284. }
  285. if (blankpoint < totalpoints / 100. * options.m_CrossPercent) continue; //空白点太少
  286. //*********************************左下区域检查**********************
  287. totalpoints = options.m_CrossLen / 2;
  288. //寻找直线左边界
  289. for (xx = -1; xx >= -options.m_CrossLen / 2; xx--){
  290. blankpoint = 0;
  291. for (yy = 1; yy <= options.m_CrossLen / 2; yy++){
  292. int posx = (int)(x + xx * dx[3][arg0] + 0.5);
  293. int posy = (int)(y + yy * dy[1][arg0] + 0.5);
  294. if (posx < 0 || posx >= width || posy < 0 || posy >= height)break;
  295. if (CV_IMAGE_ELEM(img_binary, unsigned char, posy, posx)) blankpoint++;
  296. }
  297. if (blankpoint > totalpoints * 0.5) break;
  298. }
  299. if (xx < -options.m_CrossLen / 2) continue;
  300. xx0 = xx;
  301. //寻找直线下边界
  302. for (yy = 1; yy <= options.m_CrossLen / 2; yy++){
  303. blankpoint = 0;
  304. for (xx = -1; xx >= -options.m_CrossLen / 2; xx--){
  305. int posx = (int)(x + xx * dx[3][arg0] + 0.5);
  306. int posy = (int)(y + yy * dy[1][arg0] + 0.5);
  307. if (posx < 0 || posx >= width || posy < 0 || posy >= height)break;
  308. if (CV_IMAGE_ELEM(img_binary, unsigned char, posy, posx)) blankpoint++;
  309. }
  310. if (blankpoint > totalpoints * 0.5) break;
  311. }
  312. if (yy > options.m_CrossLen / 2) continue;
  313. yy0 = yy;
  314. totalpoints = 0;
  315. blankpoint = 0;
  316. for (yy = 0; yy < options.m_CrossLen / 2; yy++){
  317. for (xx = 0; xx > -options.m_CrossLen / 2 + yy; xx--){
  318. int posx = (int)(x + (xx0 + xx) * dx[3][arg0] + 0.5);
  319. int posy = (int)(y + (yy0 + yy) * dy[1][arg0] + 0.5);
  320. if (posx < 0 || posx >= width || posy < 0 || posy >= height)break;
  321. totalpoints++;
  322. if (CV_IMAGE_ELEM(img_binary, unsigned char, posy, posx)) blankpoint++;
  323. }
  324. }
  325. if (blankpoint < totalpoints / 100. * options.m_CrossPercent) continue; //空白点太少
  326. }
  327. if (nowburr){
  328. haveburr = 1;
  329. break;
  330. }
  331. totalpoints = 0;
  332. sign_.sign = (SIGND)nowsign;
  333. sign_.x = x;
  334. sign_.y = y;
  335. result = true;
  336. for (int dir = 0; dir < 4; dir++){
  337. int breakpoints = 0;
  338. if ((nowsign & DIRECTION_FLAG[dir]) != 0)
  339. {
  340. for (int len = 1; len <= 100; len++){ //按最长边计算角度
  341. int posx = x + (int)(len * dx[dir][arg0] + 0.5);
  342. int posy = y + (int)(len * dy[dir][arg0] + 0.5);
  343. if (posx < 0 || posx >= width || posy < 0 || posy >= height)break;
  344. totalpoints++;
  345. if (CV_IMAGE_ELEM(img_binary, unsigned char, posy, posx)){ //潜在断点
  346. breakpoints++;
  347. if (breakpoints > options.m_CrossBreak) break;
  348. }
  349. else{
  350. breakpoints = 0;
  351. }
  352. }
  353. totalpoints -= breakpoints;
  354. }
  355. }
  356. }
  357. }
  358. }
  359. return result;
  360. }
  361. bool CCrossDetector::isSignPossable(const IplImage * img_binary, int x, int y)
  362. {
  363. int width = img_binary->width;
  364. int height = img_binary->height;
  365. bool result = false;
  366. if (!CV_IMAGE_ELEM(img_binary, unsigned char, y, x)){
  367. int top = 0, bottom = 0, left = 0, right = 0;
  368. {//上
  369. int ys = std::max(0, y - options.m_CrossLen);
  370. int ye = std::max(0, y - 1);
  371. int xs = std::max(0, x - 2);
  372. int xe = std::min(width - 1, x + 2);
  373. for (int yy = ys; yy <= ye; yy++)
  374. {
  375. for (int xx = xs; xx <= xe; xx++)
  376. {
  377. if (!CV_IMAGE_ELEM(img_binary, unsigned char, yy, xx)){ top++; break; }
  378. }
  379. }
  380. }
  381. {//下
  382. int ys = std::min(height - 1, y + 1);
  383. int ye = std::min(height - 1, y + options.m_CrossLen);
  384. int xs = std::max(0, x - 2);
  385. int xe = std::min(width - 1, x + 2);
  386. for (int yy = ys; yy <= ye; yy++)
  387. {
  388. for (int xx = xs; xx <= xe; xx++)
  389. {
  390. if (!CV_IMAGE_ELEM(img_binary, unsigned char, yy, xx)){ bottom++; break; }
  391. }
  392. }
  393. }
  394. {//左
  395. int ys = std::max(0, y - 2);
  396. int ye = std::min(height - 1, y + 2);
  397. int xs = std::max(0, x - options.m_CrossLen);
  398. int xe = std::max(0, x - 1);
  399. for (int xx = xs; xx <= xe; xx++)
  400. {
  401. for (int yy = ys; yy <= ye; yy++)
  402. {
  403. if (!CV_IMAGE_ELEM(img_binary, unsigned char, yy, xx)){ left++; break; }
  404. }
  405. }
  406. }
  407. {//右
  408. int ys = std::max(0, y - 2);
  409. int ye = std::min(height - 1, y + 2);
  410. int xs = std::min(width - 1, x + 1);
  411. int xe = std::min(width - 1, x + options.m_CrossLen);
  412. for (int xx = xs; xx <= xe; xx++)
  413. {
  414. for (int yy = ys; yy <= ye; yy++)
  415. {
  416. if (!CV_IMAGE_ELEM(img_binary, unsigned char, yy, xx)){ right++; break; }
  417. }
  418. }
  419. }
  420. int valve = std::max(5,options.m_CrossLen - 4);
  421. if ((left > valve || right > valve) && (top > valve || bottom > valve)){
  422. result = true;
  423. }
  424. }
  425. return result;
  426. }