utils.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. import torch
  2. import numpy as np
  3. import math
  4. import cv2
  5. import os
  6. import torchvision
  7. from PIL import Image
  8. from basic_ops import *
  9. def draw_line(y, x, angle, image, color=(0,0,255), num_directions=24):
  10. '''
  11. Draw a line with point y, x, angle in image with color.
  12. '''
  13. cv2.circle(image, (x, y), 2, color, 2)
  14. H, W = image.shape[:2]
  15. angle = int2arc(angle, num_directions)
  16. point1, point2 = get_boundary_point(y, x, angle, H, W)
  17. cv2.line(image, point1, point2, color, 2)
  18. return image
  19. def convert_line_to_hough(line, size=(32, 32)):
  20. H, W = size
  21. theta = line.angle()
  22. alpha = theta + np.pi / 2
  23. if theta == -np.pi / 2:
  24. r = line.coord[1] - W/2
  25. else:
  26. k = np.tan(theta)
  27. y1 = line.coord[0] - H/2
  28. x1 = line.coord[1] - W/2
  29. r = (y1 - k*x1) / np.sqrt(1 + k**2)
  30. return alpha, r
  31. def line2hough(line, numAngle, numRho, size=(32, 32)):
  32. H, W = size
  33. alpha, r = convert_line_to_hough(line, size)
  34. irho = int(np.sqrt(H*H + W*W) + 1) / ((numRho - 1))
  35. itheta = np.pi / numAngle
  36. r = int(np.round(r / irho)) + int((numRho) / 2)
  37. alpha = int(np.round(alpha / itheta))
  38. if alpha >= numAngle:
  39. alpha = numAngle - 1
  40. return alpha, r
  41. def line2hough_float(line, numAngle, numRho, size=(32, 32)):
  42. H, W = size
  43. alpha, r = convert_line_to_hough(line, size)
  44. irho = int(np.sqrt(H*H + W*W) + 1) / ((numRho - 1))
  45. itheta = np.pi / numAngle
  46. r = r / irho + numRho / 2
  47. alpha = alpha / itheta
  48. if alpha >= numAngle:
  49. alpha = numAngle - 1
  50. return alpha, r
  51. def reverse_mapping(point_list, numAngle, numRho, size=(32, 32)):
  52. #return type: [(y1, x1, y2, x2)]
  53. H, W = size
  54. irho = int(np.sqrt(H*H + W*W) + 1) / ((numRho - 1))
  55. itheta = np.pi / numAngle
  56. b_points = []
  57. for (thetai, ri) in point_list:
  58. theta = thetai * itheta
  59. r = ri - numRho // 2
  60. cosi = np.cos(theta) / irho
  61. sini = np.sin(theta) / irho
  62. if sini == 0:
  63. x = np.round(r / cosi + W / 2)
  64. b_points.append((0, int(x), H-1, int(x)))
  65. else:
  66. # print('k = %.4f', - cosi / sini)
  67. # print('b = %.2f', np.round(r / sini + W * cosi / sini / 2 + H / 2))
  68. angle = np.arctan(- cosi / sini)
  69. y = np.round(r / sini + W * cosi / sini / 2 + H / 2)
  70. p1, p2 = get_boundary_point(int(y), 0, angle, H, W)
  71. if p1 is not None and p2 is not None:
  72. b_points.append((p1[1], p1[0], p2[1], p2[0]))
  73. return b_points
  74. def visulize_mapping(b_points, size, filename):
  75. img = cv2.imread(os.path.join('./data/NKL', filename)) #change the path when using other dataset.
  76. # img = cv2.imread(os.path.join('./data/training/JTLEE_resize_100_100', filename)) #hanqi
  77. #img = cv2.resize(img, size)
  78. #scale_w = img.shape[1] / 400
  79. #scale_h = img.shape[0] / 400
  80. for (y1, x1, y2, x2) in b_points:
  81. #x1 = int(x1 * scale_w)
  82. #x2 = int(x2 * scale_w)
  83. #y1 = int(y1 * scale_h)
  84. #y2 = int(y2 * scale_h)
  85. img = cv2.line(img, (x1, y1), (x2, y2), (255, 255, 0), thickness=3)
  86. return img
  87. def caculate_precision(b_points, gt_coords, thresh=0.90):
  88. N = len(b_points)
  89. if N == 0:
  90. return 0, 0
  91. ea = np.zeros(N, dtype=np.float32)
  92. for i, coord_p in enumerate(b_points):
  93. if coord_p[0]==coord_p[2] and coord_p[1]==coord_p[3]:
  94. continue
  95. l_pred = Line(list(coord_p))
  96. for coord_g in gt_coords:
  97. l_gt = Line(list(coord_g))
  98. ea[i] = max(ea[i], EA_metric(l_pred, l_gt))
  99. return (ea >= thresh).sum(), N
  100. def caculate_recall(b_points, gt_coords, thresh=0.90):
  101. N = len(gt_coords)
  102. if N == 0:
  103. return 1.0, 0
  104. ea = np.zeros(N, dtype=np.float32)
  105. for i, coord_g in enumerate(gt_coords):
  106. l_gt = Line(list(coord_g))
  107. for coord_p in b_points:
  108. if coord_p[0]==coord_p[2] and coord_p[1]==coord_p[3]:
  109. continue
  110. l_pred = Line(list(coord_p))
  111. ea[i] = max(ea[i], EA_metric(l_pred, l_gt))
  112. return (ea >= thresh).sum(), N
  113. # def LinePooling(feat, coords, align_size=64):
  114. # '''
  115. # feat with shape [N, C, H, W]
  116. # coords with shape [N, K, 4], 4 means [y1, x1, y2, x2]
  117. # '''
  118. # N, C, H, W = feat.size()
  119. # scale = 1 / 4
  120. # K = coords.size(1)
  121. # assert K > 0
  122. # assert N == 1 # only support batch_size==1 in current version
  123. # for bs in range(N):
  124. # coord_st = coords[bs, :, 0:2] * scale
  125. # coord_ed = coords[bs, :, 2:4] * scale
  126. # with torch.no_grad():
  127. # arr_st2ed = coord_ed - coord_st # [K, 2]
  128. # sample_grid = torch.linspace(0, 1, align_size).to(feat).view(1, align_size).expand(K, align_size)
  129. # sample_grid = torch.einsum("id,is->isd", (arr_st2ed, sample_grid)) + coord_st.view(K, 1, 2).expand(K, align_size, 2)
  130. # sample_grid = sample_grid.view(K, align_size, 1, 2)
  131. # sample_grid[..., 0] = sample_grid[..., 0] / (H - 1) * 2 - 1
  132. # sample_grid[..., 1] = sample_grid[..., 1] / (W - 1) * 2 - 1
  133. # output = torch.nn.functional.grid_sample(feat[int(bs)].view(1, C, H, W).expand(K, C, H, W), sample_grid)
  134. # output = output.view(K, C, align_size)
  135. # return output
  136. # def gen_proposal(gt_coord, size=(400, 400)):
  137. # y1, x1, y2, x2 = gt_coord
  138. # if y1 == 0 or y1 == size[0] - 1:
  139. # delta_y1 = 0
  140. # delta_x1 = np.random.uniform(-0.02, 0.02) * size[1]
  141. # if x1 + delta_x1 >= size[1]:
  142. # delta_x1 = size[1] - 1 - x1
  143. # elif x1 + delta_x1 <= 0:
  144. # delta_x1 = 0 - x1
  145. # else:
  146. # delta_y1 = np.random.uniform(-0.02, 0.02) * size[0]
  147. # delta_x1 = 0
  148. # if y1 + delta_y1 >= size[0]:
  149. # delta_y1 = size[0] - 1 - y1
  150. # elif y1 + delta_y1 <= 0:
  151. # delta_y1 = 0 - y1
  152. # if y2 == 0 or y2 == size[0] - 1:
  153. # delta_y2 = 0
  154. # delta_x2 = np.random.uniform(-0.02, 0.02) * size[1]
  155. # if x2 + delta_x2 >= size[1]:
  156. # delta_x2 = size[1] - 1 - x2
  157. # elif x2 + delta_x2 <= 0:
  158. # delta_x2 = 0 - x2
  159. # else:
  160. # delta_y2 = np.random.uniform(-0.02, 0.02) * size[0]
  161. # delta_x2 = 0
  162. # if y2 + delta_y2 >= size[0]:
  163. # delta_y2 = size[0] - 1 - y2
  164. # elif y2 + delta_y2 <= 0:
  165. # delta_y2 = 0 - y2
  166. # return delta_y1, delta_x1, delta_y2, delta_x2
  167. #coords_ring = [] #(x, y)
  168. #size = (400, 400)
  169. #for i in range(0, size[1]):
  170. # coords_ring.append((i, 0))
  171. #for i in range(1, size[0]):
  172. # coords_ring.append((size[1]-1, i))
  173. #for i in range(size[1]-2, 0, -1):
  174. # coords_ring.append((i, size[0]-1))
  175. #for i in range(size[0]-1, 0, -1):
  176. # coords_ring.append((0, i))
  177. # def gen_proposal_consist(gt_coord):
  178. # global coords_ring
  179. # y1, x1, y2, x2 = gt_coord
  180. # step1 = int(np.random.uniform(-5, 6))
  181. # step2 = int(np.random.uniform(-5, 6))
  182. # length = len(coords_ring)
  183. # idx = coords_ring.index((x1, y1))
  184. # new_x1, new_y1 = coords_ring[(idx + step1) % length]
  185. # idx = coords_ring.index((x2, y2))
  186. # new_x2, new_y2 = coords_ring[(idx + step2) % length]
  187. # return new_y1-y1, new_x1-x1, new_y2-y2, new_x2-x2, step1, step2
  188. # def generate_proposals(gt_coords, size=(400, 400)):
  189. # # n_samples = 12 // len(gt_coords)
  190. # # n_samples = max(n_samples, 4)
  191. # n_samples = 4
  192. # proposals = []
  193. # labels = []
  194. # scale = 120
  195. # for i, gt_coord in enumerate(gt_coords):
  196. # # if i > 3: break
  197. # y1, x1, y2, x2 = gt_coord
  198. # for _ in range(n_samples):
  199. # # for j in range(1, 5):
  200. # # delta_y1, delta_x1, delta_y2, delta_x2 = gen_proposal(gt_coord, size)
  201. # delta_y1, delta_x1, delta_y2, delta_x2, step1, step2 = gen_proposal_consist(gt_coord)
  202. # proposals.append([y1+delta_y1, x1+delta_x1, y2+delta_y2, x2+delta_x2])
  203. # '''
  204. # dy1 = -delta_y1 / size[0] * scale
  205. # dx1 = -delta_x1 / size[1] * scale
  206. # dy2 = -delta_y2 / size[0] * scale
  207. # dx2 = -delta_x2 / size[1] * scale
  208. # if abs(delta_y1) < 0.001:
  209. # dy1 = 0.5
  210. # if abs(delta_x1) < 0.001:
  211. # dx1 = 0.5
  212. # if abs(delta_y2) < 0.001:
  213. # dy2 = 0.5
  214. # if abs(delta_x2) < 0.001:
  215. # dx2 = 0.5
  216. # '''
  217. # labels.append([-delta_y1 / size[0] * scale, -delta_x1 / size[1] * scale, -delta_y2 / size[0] * scale, -delta_x2 / size[1] * scale])
  218. # #labels.append([step1 / 15, step2 / 15])
  219. # #labels.append([dy1, dx1, dy2, dx2])
  220. # #labels.append([-delta_y1 / size[0] * scale, -delta_x1 / size[1] * scale, -delta_y2 / size[0] * scale, -delta_x2 / size[1] * scale])
  221. # #labels.append([-delta_y1 * scale, -delta_x1 * scale, -delta_y2 * scale, -delta_x2 * scale])
  222. # delta_y1, delta_x1, delta_y2, delta_x2 = 0, 0, 0, 0
  223. # proposals.append([y1+delta_y1, x1+delta_x1, y2+delta_y2, x2+delta_x2])
  224. # labels.append([-delta_y1 / size[0] * scale, -delta_x1 / size[1] * scale, -delta_y2 / size[0] * scale, -delta_x2 / size[1] * scale])
  225. # return proposals, labels
  226. # def add_delta(coords, deltas, size=(400, 400)):
  227. # scale = 120
  228. # deltas = deltas * 400 / scale
  229. # y1, x1, y2, x2 = coords
  230. # if left(x1, y1, size) or right(x1, y1, size):
  231. # y1 += deltas[0]
  232. # else:
  233. # x1 += deltas[1]
  234. # if left(x2, y2, size) or right(x2, y2, size):
  235. # y2 += deltas[2]
  236. # else:
  237. # x2 += deltas[3]
  238. # return y1, x1, y2, x2
  239. # def make_labels(b_points, gt_coords):
  240. # size = (400, 400)
  241. # scale = 40
  242. # pred_lines = [Line(list(coord)) for coord in b_points]
  243. # gt_lines = []
  244. # for i, coord_g in enumerate(gt_coords):
  245. # if coord_g[0]==coord_g[2] and coord_g[1]==coord_g[3]:
  246. # continue
  247. # gt_lines.append(Line(list(coord_g)))
  248. # labels = []
  249. # for i in range(len(pred_lines)):
  250. # metrics = [EA_metric(pred_lines[i], gt_line) for gt_line in gt_lines]
  251. # idx = np.argmax(metrics)
  252. # delta_y1 = b_points[i][0] - gt_coords[idx][0]
  253. # delta_x1 = b_points[i][1] - gt_coords[idx][1]
  254. # delta_y2 = b_points[i][2] - gt_coords[idx][2]
  255. # delta_x2 = b_points[i][3] - gt_coords[idx][3]
  256. # labels.append([-delta_y1 / size[0] * scale, -delta_x1 / size[1] * scale, -delta_y2 / size[0] * scale, -delta_x2 / size[1] * scale])
  257. # return labels
  258. def coords_sort(coords):
  259. y1, x1, y2, x2 = coords
  260. if x1 > x2 or (x1 == x2 and y1 > y2):
  261. yy1, xx1, yy2, xx2 = y2, x2, y1, x1
  262. else:
  263. yy1, xx1, yy2, xx2 = y1, x1, y2, x2
  264. return yy1, xx1, yy2, xx2
  265. # def left(x1, y1, size):
  266. # if x1 == 0:
  267. # return True
  268. # else:
  269. # return False
  270. # def right(x1, y1, size):
  271. # if x1 == size[1] - 1:
  272. # return True
  273. # else:
  274. # return False
  275. # def top(x1, y1, size):
  276. # if y1 == 0:
  277. # return True
  278. # else:
  279. # return False
  280. # def bottom(x1, y1, size):
  281. # if y1 == size[0] - 1:
  282. # return True
  283. # else:
  284. # return False
  285. # def generate_proposals_parametric(gt_coords, size=(400, 400)):
  286. # output_lr = -np.ones((200, 200), dtype=np.float32)
  287. # output_ud = -np.ones((200, 200), dtype=np.float32)
  288. # # mask = np.zeros((100, 100), dtype=np.float32)
  289. # for i, gt_coord in enumerate(gt_coords):
  290. # y1, x1, y2, x2 = gt_coord
  291. # if y1 == y2 and x1 == x2:
  292. # continue
  293. # gt_l = Line([y1, x1, y2, x2])
  294. # theta, r = line2hough(gt_l, 200, 200, size)
  295. # for i in range(theta-50, theta+51):
  296. # for j in range(r-50, r+51):
  297. # if i < 0 or i >= 200:
  298. # continue
  299. # if j < 0 or j >= 200:
  300. # continue
  301. # # if i == theta and j == r:
  302. # # continue
  303. # # if i != theta:
  304. # # mask[0, i, j] = 1
  305. # # if j != r:
  306. # # mask[1, i, j] = 1
  307. # if i < theta:
  308. # output_lr[i, j] = 0
  309. # elif i > theta:
  310. # output_lr[i, j] = 1
  311. # else:
  312. # output_lr[i, j] = -1
  313. # if j < r:
  314. # output_ud[i, j] = 0
  315. # elif i > theta:
  316. # output_ud[i, j] = 1
  317. # else:
  318. # output_ud[i, j] = -1
  319. # # output[0, i, j] = (i<theta) * 1.0
  320. # output[1, i, j] = (j<r) * 1.0
  321. # if theta-1>=0:
  322. # mask[theta-1, r] = 1
  323. # output[0, theta-1, r] = 0
  324. # if theta+1 < 100:
  325. # mask[theta+1, r] = 1
  326. # output[0, theta+1, r] = 1
  327. # if r-1>=0:
  328. # mask[theta, r-1] = 1
  329. # output[1, theta, r-1] = 0
  330. # if r+1 < 100:
  331. # mask[theta, r+1] = 1
  332. # output[1, theta, r+1] = 1
  333. # if theta-2>=0:
  334. # mask[theta-2, r] = 1
  335. # output[0, theta-2, r] = 0
  336. # if theta+2<100:
  337. # mask[theta+2, r] = 1
  338. # output[0, theta+2, r] = 1
  339. # if r-2>=0:
  340. # mask[theta, r-2] = 1
  341. # output[1, theta, r-2] = 0
  342. # if r+2<100:
  343. # mask[theta, r+2] = 1
  344. # output[1, theta, r+2] = 1
  345. # if theta-3>=0:
  346. # mask[theta-3, r] = 1
  347. # output[0, theta-3, r] = 0
  348. # if theta+3<100:
  349. # mask[theta+3, r] = 1
  350. # output[0, theta+3, r] = 1
  351. # if r-3>=0:
  352. # mask[theta, r-3] = 1
  353. # output[1, theta, r-3] = 0
  354. # if r+3<100:
  355. # mask[theta, r+3] = 1
  356. # output[1, theta, r+3] = 1
  357. # return output_lr, output_ud
  358. def get_density(filename, x1, y1, x2, y2):
  359. hed_path = '/home/hanqi/JTLEE_code/pytorch-hed/hed_results/'
  360. filename = filename.split('_')[0]
  361. hed_file_path = os.path.join(hed_path, filename + '.png')
  362. hed = np.array(Image.open(hed_file_path).convert('L')) / 255
  363. mask = np.zeros_like(hed)
  364. mask = cv2.line(mask, (x1, y1), (x2, y2), color=1.0, thickness=7)
  365. density = (mask * hed).sum() / mask.sum()
  366. return density
  367. def local_search(coords, coords_ring, d=1):
  368. y1, x1 = coords
  369. length = len(coords_ring)
  370. idx = coords_ring.index((x1, y1))
  371. new_x1, new_y1 = coords_ring[(idx + d) % length]
  372. return new_y1, new_x1
  373. def overflow(x, size=400):
  374. return x < 0 or x >= size
  375. def edge_align(coords, filename, size, division=9):
  376. y1, x1, y2, x2 = coords
  377. ry1, rx1, ry2, rx2 = y1, x1, y2, x2
  378. if overflow(y1, size[0]) or overflow(x1, size[1]) or overflow(y2, size[0]) or overflow(x2, size[1]):
  379. return [ry1, rx1, ry2, rx2]
  380. density = 0
  381. hed_path = './data/sl6500_hed_results/'
  382. # hed_path = '/home/hanqi/JTLEE_code/pytorch-hed/hed_results/'
  383. filename = filename.split('.')[0]
  384. hed_file_path = os.path.join(hed_path, filename + '.png')
  385. hed = np.array(Image.open(hed_file_path).convert('L')) / 255
  386. coords_ring = [] #(x, y)
  387. #size = (400, 400)
  388. for i in range(0, size[1]):
  389. coords_ring.append((i, 0))
  390. for i in range(1, size[0]):
  391. coords_ring.append((size[1]-1, i))
  392. for i in range(size[1]-2, 0, -1):
  393. coords_ring.append((i, size[0]-1))
  394. for i in range(size[0]-1, 0, -1):
  395. coords_ring.append((0, i))
  396. for d1 in range(-division, division+1):
  397. for d2 in range(-division, division+1):
  398. ny1, nx1 = local_search([y1, x1], coords_ring, d=d1)
  399. ny2, nx2 = local_search([y2, x2], coords_ring, d=d2)
  400. mask = np.zeros_like(hed)
  401. mask = cv2.line(mask, (nx1, ny1), (nx2, ny2), color=1.0, thickness=3)
  402. dens = (mask * hed).sum() / mask.sum()
  403. if dens > density:
  404. density = dens
  405. ry1, rx1, ry2, rx2 = ny1, nx1, ny2, nx2
  406. return [ry1, rx1, ry2, rx2]