I am trying to build a system to detect bullet holes in paper. I've read many StackOverflow threads but not a single one satisfied me. Current methods, which I am using, are good but not perfect and not fully reliable. It often misses couple of shots or has problems with calibration when live testing.
System requirements:
- Realtime detection,
- Has very low input lag between bullet making hole in paper and detection,
- minimal screen size - 2.5m x 1.5m,
- works with .22, 9x19, 5.56x45, 7.62x39 etc. ammunition,
- returns X and Y coordinates of detected bullet hit.
What I am currently using:
- Arducam IMX477 12,3MPx HQ + usb 2.0 interface to simulate webcam,
- Camera lens - which is terrible :P but i needed zoom function, visible light filter on camera and IR lights - to be independent from enviroment lights.
- Python detection:
- Image subtraction - monitoring changes,
- Thresholding an image from camera - to eliminate noise,
- Detection - OpenCV.findContours or analysis of numpy array in search of high values in some region of image.
Camera is located 4.5-5m from screen. I'm detecting black areas that appear on following frames by watching for changes between these frames and finding maximum if some big spike (2 times greater than noise) is detected.
Here is some code example and nice plots that I've made:
import cv2
import time
video_stream = cv2.VideoCapture('C:\Projekty\HoleDetection\data\IRNEW\WIN_20220204_14_26_23_Pro_Trim.mp4')
prev_frame = None
i = 0
start = float(time.time())
while video_stream.isOpened():
ret, frame = video_stream.read()
if not ret:
break
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
if prev_frame is not None:
subtracted_frame = cv2.subtract(prev_frame, frame)
# cv2.imshow('subtr', cv2.resize(subtracted_frame, (1280, 720)))
ax_0_max = subtracted_frame.max(axis=0)
ax_1_max = subtracted_frame.max(axis=1)
curr_max = ax_1_max.max()
if prev_max * 2 < curr_max:
print('Detected shot')
x = ax_0_max.argmax()
y = ax_1_max.argmax()
print(f'X: {x}, Y: {y}')
# keypoints.append(cv2.KeyPoint(float(x), float(y), 10))
prev_max = curr_max
else:
prev_max = frame.max()
prev_frame = frame
# frame = cv2.drawKeypoints(frame, keypoints, np.array([]), (0, 0, 255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
# cv2.imshow('im', cv2.resize(frame, (1280, 720)))
i += 1
if cv2.waitKey(1) == ord('q'):
break
end = float(time.time())
cv2.destroyAllWindows()
print(f'Time {end-start} for {i+1} frames. FPS: {(i+1)/(end-start)}')
Whole movie analysis:
Keyframe where first bullet hole appeared:
This is how it looks like from camera perspective. Little bit blurry and fish lens effect. It's not ideal. It works almost perfect (or maybe just good) with 9x19 ammunition, but has problems with smaller calliber (.22, .223Rem, 5.56x45) - changes are less noticeable because of smaller holes.
Notice one unfortunate thing - I can't put anything behind the screen because it's shelled with live ammunition.
I would like to hear from you how to improve this system. Maybe use some kind of sensors that are capable of detecting bullet holes (if there is such a thing) and more reliable than current methods or maybe there is some better python method available