title: How to do OpenCV Laser shot detection with Python on light backgrounds?
body: <p>I'm trying to build a laser shot detection game in Python3 using OpenCV.  I have a proof of concept working which will highlight detected momentary &quot;shots&quot; of the laser on the background.</p>
  <p>The problem I'm having is that the laser signature doesn't get detected on lighter backgrounds, or if there is a very white / bright color item near the shot.  I'm sure this is because the way I'm using binary thresholds to detect the laser as the brightest thing in the frame, and with light elements the laser gets washed out.</p>
  <p>My question is how can I alter my approach to handle this situation, or perhaps &quot;calibrate&quot; the background / other items so that the laser can be detected?  Ultimately I'm trying to detect laser shots on a computer screen, where the background where shots are landing is a video with its own high lights and the screen puts out its own light.</p>
  <p>Any guidance is appreciated.</p>
  <p><strong>main.py</strong></p>
  <pre class="lang-py prettyprint-override"><code>import cv2
  from camera import VideoCamera
  from detection import LaserDetector
  
  debug=False
  radius_min = float(1)
  radius_max = float(10)
  shot_size = 5
  color_blue = (255, 0, 0)
  
  cam = VideoCamera(640, 480)
  shots = []
  
  try:
      while(True):
          frame = cam.get_frame()
  
          if frame is not False:
              shot = LaserDetector(frame, debug).detect()
  
              if shot:
                  x, y, radius = shot
  
                  if radius &gt;= radius_min and radius &lt;= radius_max:
                      shots.append(shot)
                      if debug:
                          print(f&quot;(x:{int(x)}, y:{int(y)}, r:{int(radius)})&quot;)
  
              for s in shots:
                  cv2.circle(frame, (int(s[0]), int(s[1])), shot_size, color_blue, 1)
  
              cv2.imshow('frame', frame)
  
          if cv2.waitKey(1) &amp; 0xFF == ord('q'):
              break
  
  except KeyboardInterrupt:
      pass
  finally:
      print(&quot;\nClosing video capture...&quot;)
      cam.release()
      cv2.destroyAllWindows()
  </code></pre>
  <p><strong>camera.py</strong></p>
  <pre class="lang-py prettyprint-override"><code>import cv2
  class VideoCamera(object):
      def __init__(self, cam_width, cam_height):
          self.capture = cv2.VideoCapture(0)
          self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, cam_width)
          self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, cam_height)
  
      def release(self):
          self.capture.release()
  
      def __del__(self):
          self.capture.release()
  
      def get_frame(self):
          success, frame = self.capture.read()
          if success:
              return frame
          else:
              return False
  
      def frame_to_jpeg(self, frame):
          ret, jpeg = cv2.imencode('.jpg', frame)
          return jpeg.tobytes()
  </code></pre>
  <p><strong>detection.py</strong></p>
  <pre class="lang-py prettyprint-override"><code>import cv2
  import numpy as np
  import decimal
  
  class LaserDetector(object):
  
      def __init__(self, frame, debug=False):
          self.debug = debug
          self.frame = frame
  
  
      def get_contour_points(self, mask):
          countours = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
          if len(countours) &gt; 0:
              # find the largest contour in the mask, then use
              # it to compute the minimum enclosing circle and
              c = max(countours, key=cv2.contourArea)
              ((x, y), radius) = cv2.minEnclosingCircle(c)
              moments = cv2.moments(c)
              if moments[&quot;m00&quot;] &gt; 0:
                  # set the center
                  (x,y) = int(moments[&quot;m10&quot;] / moments[&quot;m00&quot;]), \
                              int(moments[&quot;m01&quot;] / moments[&quot;m00&quot;])
  
              radius = round(decimal.Decimal(radius), 3)
              return (int(x), int(y), radius)
  
          return False
  
  
      def get_hsv_threshold_mask(self, frame):
          hsv_image = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
          h, s, v = cv2.split(hsv_image)
          ret, h_frame = cv2.threshold(h, 125, 160, cv2.THRESH_BINARY)
          ret, v_frame = cv2.threshold(v, 250, 256, cv2.THRESH_BINARY)
          output = cv2.bitwise_and(h_frame, v_frame, frame)
  
          if self.debug:
              indiv_output = np.concatenate((h_frame, v_frame, output), axis=1)
              cv2.imshow(&quot;threshold&quot;, indiv_output)
  
          return output
  
      def detect(self):
          mask = self.get_hsv_threshold_mask(self.frame)
          return self.get_contour_points(mask)
  </code></pre>