rendering.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import math
  2. import numpy as np
  3. def downsample(img, factor):
  4. """
  5. Downsample an image along both dimensions by some factor
  6. """
  7. assert img.shape[0] % factor == 0
  8. assert img.shape[1] % factor == 0
  9. img = img.reshape(
  10. [img.shape[0] // factor, factor, img.shape[1] // factor, factor, 3]
  11. )
  12. img = img.mean(axis=3)
  13. img = img.mean(axis=1)
  14. return img
  15. def fill_coords(img, fn, color):
  16. """
  17. Fill pixels of an image with coordinates matching a filter function
  18. """
  19. for y in range(img.shape[0]):
  20. for x in range(img.shape[1]):
  21. yf = (y + 0.5) / img.shape[0]
  22. xf = (x + 0.5) / img.shape[1]
  23. if fn(xf, yf):
  24. img[y, x] = color
  25. return img
  26. def rotate_fn(fin, cx, cy, theta):
  27. def fout(x, y):
  28. x = x - cx
  29. y = y - cy
  30. x2 = cx + x * math.cos(-theta) - y * math.sin(-theta)
  31. y2 = cy + y * math.cos(-theta) + x * math.sin(-theta)
  32. return fin(x2, y2)
  33. return fout
  34. def point_in_line(x0, y0, x1, y1, r):
  35. p0 = np.array([x0, y0], dtype=np.float32)
  36. p1 = np.array([x1, y1], dtype=np.float32)
  37. dir = p1 - p0
  38. dist = np.linalg.norm(dir)
  39. dir = dir / dist
  40. xmin = min(x0, x1) - r
  41. xmax = max(x0, x1) + r
  42. ymin = min(y0, y1) - r
  43. ymax = max(y0, y1) + r
  44. def fn(x, y):
  45. # Fast, early escape test
  46. if x < xmin or x > xmax or y < ymin or y > ymax:
  47. return False
  48. q = np.array([x, y])
  49. pq = q - p0
  50. # Closest point on line
  51. a = np.dot(pq, dir)
  52. a = np.clip(a, 0, dist)
  53. p = p0 + a * dir
  54. dist_to_line = np.linalg.norm(q - p)
  55. return dist_to_line <= r
  56. return fn
  57. def point_in_circle(cx, cy, r):
  58. def fn(x, y):
  59. return (x - cx) * (x - cx) + (y - cy) * (y - cy) <= r * r
  60. return fn
  61. def point_in_rect(xmin, xmax, ymin, ymax):
  62. def fn(x, y):
  63. return x >= xmin and x <= xmax and y >= ymin and y <= ymax
  64. return fn
  65. def point_in_triangle(a, b, c):
  66. a = np.array(a, dtype=np.float32)
  67. b = np.array(b, dtype=np.float32)
  68. c = np.array(c, dtype=np.float32)
  69. def fn(x, y):
  70. v0 = c - a
  71. v1 = b - a
  72. v2 = np.array((x, y)) - a
  73. # Compute dot products
  74. dot00 = np.dot(v0, v0)
  75. dot01 = np.dot(v0, v1)
  76. dot02 = np.dot(v0, v2)
  77. dot11 = np.dot(v1, v1)
  78. dot12 = np.dot(v1, v2)
  79. # Compute barycentric coordinates
  80. inv_denom = 1 / (dot00 * dot11 - dot01 * dot01)
  81. u = (dot11 * dot02 - dot01 * dot12) * inv_denom
  82. v = (dot00 * dot12 - dot01 * dot02) * inv_denom
  83. # Check if point is in triangle
  84. return (u >= 0) and (v >= 0) and (u + v) < 1
  85. return fn
  86. def highlight_img(img, color=(255, 255, 255), alpha=0.30):
  87. """
  88. Add highlighting to an image
  89. """
  90. blend_img = img + alpha * (np.array(color, dtype=np.uint8) - img)
  91. blend_img = blend_img.clip(0, 255).astype(np.uint8)
  92. img[:, :, :] = blend_img