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

88 line
2.5KB

  1. import math
  2. import time
  3. import itertools as it
  4. import cv2
  5. import colorsys
  6. from pythonosc import udp_client
  7. import numpy as np
  8. DENSITY = 4
  9. RED = [0, 0, 255]
  10. N_COLORS = 3
  11. LAST_MESSAGE_TIME = 0
  12. def draw_rectangle(frame, sp, ep):
  13. """ Draw a rectangle on the frame """
  14. return cv2.rectangle(frame, sp, ep, RED, 1)
  15. def analyze_block(frame, osc, index, sp, ep, send=False):
  16. """ Analyze a block """
  17. block = frame[sp[1]:ep[1], sp[0]:ep[0], 0:3]
  18. average_color = np.average(block, (0, 1)) / 255
  19. h, s, v = colorsys.rgb_to_hsv(*average_color)
  20. # Configure the oscillator
  21. if send:
  22. print("Sending message", h, s, v)
  23. osc.send_message(
  24. "/radio",
  25. [int(index), float(h),
  26. float(s), float(v), .5])
  27. # Draw a blob of the average color
  28. sp2 = tuple(int(x) for x in (2 * np.array(sp) + np.array(ep)) / 3)
  29. ep2 = tuple(int(x) for x in (np.array(sp) + 2 * np.array(ep)) / 3)
  30. frame = cv2.rectangle(frame, sp2, ep2,
  31. [int(x * 255) for x in average_color], -1)
  32. # Draw the image
  33. for thickness, color in ((3, (0, 0, 0)), (1, (255, 255, 255))):
  34. frame = cv2.putText(frame,
  35. text=f"{h:.2f} {s:.2f} {v:.2f}",
  36. org=(sp[0] + 10, sp[1] + 20),
  37. fontScale=.5,
  38. color=color,
  39. fontFace=2,
  40. thickness=thickness)
  41. return frame
  42. def analyze(frame, osc):
  43. """ Analyze the full frame """
  44. global LAST_MESSAGE_TIME
  45. height, width, d = frame.shape
  46. n = DENSITY
  47. m = math.ceil(n * (height / width))
  48. dx = width / n
  49. dy = height / m
  50. send = False
  51. if time.time() - LAST_MESSAGE_TIME > 0.1:
  52. LAST_MESSAGE_TIME = time.time()
  53. send = True
  54. for index, (y, x) in enumerate(it.product(range(m), range(n))):
  55. sp = (int(x * dx), int(y * dy))
  56. ep = (int(x * dx + dx), int(y * dy + dy))
  57. frame = draw_rectangle(frame, sp, ep)
  58. frame = analyze_block(frame, osc, index, sp, ep, send)
  59. return frame
  60. if __name__ == '__main__':
  61. camera = cv2.VideoCapture("/dev/video2")
  62. # camera = cv2.VideoCapture(0)
  63. osc = udp_client.SimpleUDPClient("0.0.0.0", 5005)
  64. while True:
  65. ret, frame = camera.read()
  66. frame = analyze(frame, osc)
  67. cv2.imshow('Input', frame)
  68. c = cv2.waitKey(1)
  69. if c == 27:
  70. break
  71. camera.release()
  72. cv2.destroyAllWindows()