Skip to content

Commit

Permalink
Merge pull request #57 from springbok/updates
Browse files Browse the repository at this point in the history
Updates to enable connector to work with new Rapsodo Range
  • Loading branch information
springbok authored Dec 20, 2023
2 parents 6990abc + 72ccc5a commit ecdebe3
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 42 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,5 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
train.traineddata.new
train.traineddata.old
2 changes: 1 addition & 1 deletion src/MainWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class LogTableCols:


class MainWindow(QMainWindow, Ui_MainWindow):
version = 'V1.01.16'
version = 'V1.01.20'
app_name = 'MLM2PRO-GSPro-Connector'
good_shot_color = '#62ff00'
good_putt_color = '#fbff00'
Expand Down
1 change: 1 addition & 0 deletions src/SelectDeviceForm.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def __init__(self, main_window: MainWindow):


def showEvent(self, event: PySide6.QtGui.QShowEvent) -> None:
self.devices.load_devices()
# Load data into the table
self.__load_device_table()
self.devices_table.selectRow(0)
Expand Down
74 changes: 40 additions & 34 deletions src/ball_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,15 @@ def process_putt_data(self, ocr_result, roi, previous_balldata):
msg = None
result = ''
try:
# Strip non ascii chars
ocr_result = re.sub(r'[^\x00-\x7f]',r'', ocr_result)
logging.debug(f'remove non ASCII {roi}: {ocr_result}')
cleaned_result = re.findall(r"[LR]?(?:\d*\.*\d)", ocr_result)
if isinstance(cleaned_result, list or tuple) and len(cleaned_result) > 0:
cleaned_result = cleaned_result[0]
if len(cleaned_result) > 0:
# Remove any leading '.' sometimes a - is read as a '.'
result = result.lstrip('.')
result = cleaned_result.strip()
# Check values are not 0
if roi == BallMetrics.SPEED:
Expand Down Expand Up @@ -207,40 +212,41 @@ def process_shot_data(self, ocr_result, roi, previous_balldata, launch_monitor):
cleaned_result = re.findall(r"[-+]?(?:\d*\.*\d+)[LR]?", ocr_result)
if isinstance(cleaned_result, list or tuple) and len(cleaned_result) > 0:
cleaned_result = cleaned_result[0]
result = cleaned_result.strip()
# Remove any leading '.' sometimes a - is read as a '.'
result = result.lstrip('.')
if launch_monitor == LaunchMonitor.MEVOPLUS and (roi == BallMetrics.HLA or roi == BallMetrics.SPIN_AXIS):
result = result.upper()
if result.endswith('L'):
result = -float(result[:-1])
if len(cleaned_result) > 0:
result = cleaned_result.strip()
# Remove any leading '.' sometimes a - is read as a '.'
result = result.lstrip('.')
if launch_monitor == LaunchMonitor.MEVOPLUS and (roi == BallMetrics.HLA or roi == BallMetrics.SPIN_AXIS):
result = result.upper()
if result.endswith('L'):
result = -float(result[:-1])
else:
result = float(result[:-1])
else:
result = float(result[:-1])
else:
result = float(result)
# Check values are not 0
if roi in BallData.must_not_be_zero and result == float(0):
logging.debug(f"Value for {BallData.properties[roi]} is 0")
raise ValueError(f"Value for '{BallData.properties[roi]}' is 0")
# For some reason ball speed sometimes get an extra digit added
if roi == BallMetrics.SPEED and result > 200:
result = self.__fix_out_of_bounds_metric(200, result, roi)
elif roi == BallMetrics.TOTAL_SPIN and result > 15000:
result = self.__fix_out_of_bounds_metric(15000, result, roi)
elif roi == BallMetrics.CLUB_SPEED and result > 140:
result = self.__fix_out_of_bounds_metric(140, result, roi)
# Round to one decimal place
setattr(self, roi, math.floor(result*10)/10)
logging.debug(f'Cleaned and corrected value: {result}')
# Check previous ball data if required
if not self.new_shot:
if not previous_balldata is None:
previous_metric = getattr(previous_balldata, roi)
logging.debug(f'previous_metric: {previous_metric} result: {result}')
if previous_metric != result:
result = float(result)
# Check values are not 0
if roi in BallData.must_not_be_zero and result == float(0):
logging.debug(f"Value for {BallData.properties[roi]} is 0")
raise ValueError(f"Value for '{BallData.properties[roi]}' is 0")
# For some reason ball speed sometimes get an extra digit added
if roi == BallMetrics.SPEED and result > 200:
result = self.__fix_out_of_bounds_metric(200, result, roi)
elif roi == BallMetrics.TOTAL_SPIN and result > 15000:
result = self.__fix_out_of_bounds_metric(15000, result, roi)
elif roi == BallMetrics.CLUB_SPEED and result > 140:
result = self.__fix_out_of_bounds_metric(140, result, roi)
# Round to one decimal place
setattr(self, roi, math.floor(result*10)/10)
logging.debug(f'Cleaned and corrected value: {result}')
# Check previous ball data if required
if not self.new_shot:
if not previous_balldata is None:
previous_metric = getattr(previous_balldata, roi)
logging.debug(f'previous_metric: {previous_metric} result: {result}')
if previous_metric != result:
self.new_shot = True
else:
self.new_shot = True
else:
self.new_shot = True
except ValueError as e:
msg = f'{format(e)}'
except:
Expand Down Expand Up @@ -284,9 +290,9 @@ def check_smash_factor(self):
corrected_value = math.floor(ball_speed/10)
setattr(self, BallMetrics.SPEED, corrected_value)
logging.debug(f"Invalid smash factor value: {smash_factor} > 1.7, corrected {BallData.properties[BallMetrics.SPEED]} value: {corrected_value}")
elif smash_factor < 0.7:
elif smash_factor < 0.6:
corrected_value = math.floor(club_speed/10)
setattr(self, BallMetrics.CLUB_SPEED, corrected_value)
logging.debug(f"Invalid smash factor value: {smash_factor} < 0.7, corrected {BallData.properties[BallMetrics.CLUB_SPEED]} value: {corrected_value}")
logging.debug(f"Invalid smash factor value: {smash_factor} < 0.6, corrected {BallData.properties[BallMetrics.CLUB_SPEED]} value: {corrected_value}")


4 changes: 2 additions & 2 deletions src/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ def __init__(self, app_paths: AppDataPaths):
self.app_paths = app_paths
self.devices = []
self.__create_default_devices()
self.__load_devices()
self.load_devices()

def __load_devices(self):
def load_devices(self):
logging.debug(f'Checking for device config files in {self.app_paths.app_data_path}')
self.devices = []
i = 1
Expand Down
45 changes: 40 additions & 5 deletions src/screenshot_base.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import logging
import os
import time
import numpy as np
import pyqtgraph as pg
import tesserocr
from PIL import Image
from PIL import Image, ImageOps
from pyqtgraph import ViewBox
from src.ball_data import BallData
from src.labeled_roi import LabeledROI
Expand Down Expand Up @@ -120,6 +118,7 @@ def mse(self, imageA, imageB):
def ocr_image(self):
self.balldata = BallData()
self.new_shot = False
fallback_tesserocr_api = None
if self.__class__.__name__ == 'ScreenshotExPutt':
train_file = 'exputt'
else:
Expand All @@ -129,19 +128,55 @@ def ocr_image(self):
logging.debug(f"Using {train_file}_traineddata for OCR")
tesserocr_api = tesserocr.PyTessBaseAPI(psm=tesserocr.PSM.SINGLE_WORD, lang=train_file, path='.\\')
try:
#pil_img = Image.fromarray(self.screenshot_image).convert('L')
#sc = np.array(pil_img)
for roi in self.rois_properties():
cropped_img = self.image_rois[roi].getArrayRegion(self.screenshot_image, self.image_item)
#original_height, original_width = cropped_img.shape[:2]
#print(f"width: {original_width} height: {original_height}")
#factor = 3
#cropped_img = cv2.resize(cropped_img,
# (int(original_height * 12), int(original_width * 2)),
# interpolation=cv2.INTER_LINEAR)
img = Image.fromarray(np.uint8(cropped_img))
#width, height = img.size
#img = img.resize(int(width * factor), int(height * factor))
if self.__class__.__name__ != 'ScreenshotExPutt' and self.settings.device_id == LaunchMonitor.MLM2PRO:
# Convert to black text on white background, remove background
threshold = 180
threshold = 150
img = img.point(lambda x: 0 if x > threshold else 255)
#filename = time.strftime(f"{roi}_%Y%m%d-%H%M%S.bmp")
#filename = time.strftime(f"{roi}.bmp")
#path = f"{os.getcwd()}\\appdata\\logs\\original_{filename}"
#img.save(path)
bbox = ImageOps.invert(img).getbbox()
logging.debug(f'ocr {roi} - bounding box for white space removal: {bbox}')
bbox1 = []
if bbox is not None:
for i in range(len(bbox)):
if (i == 0 or i == 1) and bbox[i] > 0: # left & upper
bbox1.append(bbox[i] - 5)
elif (i == 2 or i == 3): # right & lower
bbox1.append(bbox[i] + 5)
else:
bbox1.append(bbox[i])
logging.debug(f'ocr {roi} - modified bounding box with a small amount of white space added: {bbox1}')
img = img.crop(bbox1)
#filename = time.strftime(f"{roi}.bmp")
#path = f"{os.getcwd()}\\appdata\\logs\\{filename}"
#img.save(path)
tesserocr_api.SetImage(img)
ocr_result = tesserocr_api.GetUTF8Text()
logging.debug(f'ocr {roi}: {ocr_result}')
conf = tesserocr_api.MeanTextConf()
logging.debug(f'ocr {roi} - confidence: {conf} result: {ocr_result}')
if conf <= 0:
logging.debug(f'ocr {roi} confidence <= 0 retrying with RAW_LINE')
if fallback_tesserocr_api is None:
fallback_tesserocr_api = tesserocr.PyTessBaseAPI(psm=tesserocr.PSM.RAW_LINE, lang=train_file, path='.\\')
fallback_tesserocr_api.SetImage(img)
ocr_result = fallback_tesserocr_api.GetUTF8Text()
conf = fallback_tesserocr_api.MeanTextConf()
logging.debug(f'fallback ocr {roi} - confidence: {conf} result: {ocr_result}')
if self.__class__.__name__ == 'ScreenshotExPutt':
self.balldata.process_putt_data(ocr_result, roi, self.previous_balldata)
else:
Expand Down
Binary file modified train.traineddata
Binary file not shown.
Binary file added train.traineddata.old
Binary file not shown.

0 comments on commit ecdebe3

Please sign in to comment.