Archery MOA Calculator


Instructions:
To play demo target, click "Play Demo" button.
To upload your own target file using the User Upload form below.

Default settings:
Target size (cm) = 40
Range (m) = 18

Demo


User Upload


<?php
/*
    insert.php
    Znamensky | 2021
*/



    $db = mysqli_connect("", "", "", "");

    // demo
    if (isset($_POST['demo'])) {
        $tmp = exec("python3 generatepts.py");
        $command = escapeshellcmd($tmp);
        foreach(glob('images/*.*') as $filename){
            $target_filename = substr($filename,7);
            $target = "images/" . $target_filename;
        }
    }

    // User Upload
    if (isset($_POST['upload'])) {
        $image = $_FILES['image']['name'];
        $target_filename = basename($image);
        $target = "images/".basename($image);
        $sql = "INSERT INTO images (image) VALUES ('$image')";
        mysqli_query($db, $sql);
    }


    $output_path2 = strval($target_filename) ;
    //exec function
    $tmp = exec("python3 runner.py $output_path2");
    //escapeshell
    $command = escapeshellcmd($tmp);
    //builds output HTML
    echo "<!DOCTYPE html><html><head></head><body>";
    $output_path1 = '<img src="output/';
    $output_path = $output_path1 . $output_path2 . '">' ;
    echo "$output_path";
    echo "$tmp";
    echo "</body></html>";

    ?>

?>





/*
    runner.py
    Znamensky | 2021
*/

#!/usr/bin/env python3
from background_subtraction_PIL import generateDifference
from circle_detection_opencv import image_to_circle_list
from smallestenclosingcircletest import _smallest_enclosing_circle_naive
from drawcircle import drawCircle
import sys
import math

# takes input from PHP
userfile = str(sys.argv[1])

# Main File: background_subtraction_PIL
# takes before and after images and generates new image showing differences as "diff.jpg"
img_before = 'stock_images/archery_before.jpg'
img_after = 'images/' + userfile

generateDifference(img_before,img_after)

# Main File: circle_detection_opencv
# image -> circle detection -> numpy array -> list of tuples [(a0,b0),(a1,b1),...]
points = image_to_circle_list()
print("runner points",points)

# Main File: smallestenclosingcircletest
#tuple list -> (x,y,r) of min enclosing circle
circledata = _smallest_enclosing_circle_naive(points)
print("circledata",circledata)
x = circledata[0]
y = circledata[1]
r = circledata[2]

# target size = 40cm, or 0.4m
targetsize = 0.4
userdistance = 18

x_real = round((x/1000)*targetsize,2)
y_real = round((y/1000)*targetsize,2)
r_real = round((r/1000)*targetsize,2)

angle = math.degrees(math.atan(r_real/userdistance))
MOA = round(angle*60*2,2)

# Main File: drawcircle
# draws circle overlayed on original after_image
drawCircle(x,y,r,userfile)

#output statements
print("Target Size", targetsize, "m | ", "Distance", userdistance, "m | ","Circle_x=", x_real, "m | ", "Circle_y=", y_real, "m | ", "Circle_radius=", r_real, "m | ", "MOA=",MOA)



/*
    generatepts.py
    Znamensky | 2021
*/


import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Circle
import matplotlib.cbook as cbook
import os

#Save space
mypath = 'images/'
onlyfiles = [f for f in os.listdir(mypath) if os.path.isfile(os.path.join(mypath, f))]
onlyfiles_sorted = sorted(onlyfiles)
for i in range(0,len(onlyfiles_sorted)):
	file = mypath + onlyfiles_sorted[i]
	os.remove(file)

#Read empty image file (no pts)
img = plt.imread('stock_images/archery_before.jpg')

#Generate random number pts,z < 50 with x,y < image dims
z = np.random.randint(50)
x = np.random.rand(z)*img.shape[1]
y = np.random.rand(z)*img.shape[0]
fig,ax = plt.subplots(1)
ax.set_aspect('equal')
origin = 500

for xx,yy in zip(x,y):
    r = ((xx-origin)**2 + (yy-origin)**2)**0.5
    if r < 470: #aesthetic use only - prevents points on edges from partial cut-off
        if 300 < r < 400: # recolors pts landing in black area to white for visibility
            circ = Circle((xx,yy),10, color='white')
        else:
            circ = Circle((xx,yy),10, color='black')
        ax.add_patch(circ)

ax.imshow(img)
dpi = 100
ax.axis('off')
#Static file may cause collisions, esp. w/caching.
#Therefore, random file signatures used.
g = np.random.randint(50)
save_directory = 'images/demo' + str(g) + '.jpg'
plt.savefig(save_directory, dpi=dpi)




/*
    background_subtraction_PIL.py
    Znamensky | 2021
*/

#!/usr/bin/env python3
from PIL import Image, ImageChops

def generateDifference(img_before,img_after):
    img1 = Image.open(img_before)
    img2 = Image.open(img_after)
    diff = ImageChops.difference(img1,img2)

    if diff.getbbox():
        diff.save("diff.jpg","JPEG")







/*
    circle_detection_opencv.py
    Znamensky | 2021
*/


#!/usr/bin/env python3
import smallestenclosingcircletest
import numpy as np
import cv2 as cv

def image_to_circle_list():
    img = cv.imread('diff.jpg')
    img_invert = (255-img)
    output = img_invert.copy()
    gray = cv.cvtColor(img_invert, cv.COLOR_BGR2GRAY)
    minDistance = 10
    circles = cv.HoughCircles(gray, cv.HOUGH_GRADIENT, 1, minDistance,
                              param1=50, param2=9, minRadius=0, maxRadius=15)
    detected_circles = np.uint16(np.around(circles))


    ####################### Graphical Display ########################


    #for (x, y ,r) in detected_circles[0, :]:
    #    cv.circle(output, (x, y), r, (0, 255, 0), 3)
    #    cv.circle(output, (x, y), 2, (0, 255, 255), 3)
    #cv.imshow('output',output)
    #cv.waitKey(5000)
    #cv.destroyAllWindows()

    ####################### Graphical Display ########################

    points = []
    detected_circles = detected_circles[0]
    num_rows, num_cols = detected_circles.shape

    for i in range(num_rows):
        x = int(detected_circles[i][0])
        y = int(detected_circles[i][1])
        circletuple = (x,y)
        points.append(circletuple)

    return points




/*
    drawcircle.py
    Znamensky | 2021
*/


#!/usr/bin/env python3
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Circle

def drawCircle(x,y,r,userfile):
	img_after = 'images/' + userfile
	img = plt.imread(img_after)
	fig,ax = plt.subplots(1)
	ax.set_aspect('equal')
	ax.imshow(img)
	circ = Circle((x,y),r, color = "#008000", fill=False)
	ax.add_patch(circ)
	outputdir = 'output/' + userfile
	plt.savefig(outputdir)