Skip to content

Commit

Permalink
Runmaxx 9.1 Threadmill on Win 10 (Issue #1581)
Browse files Browse the repository at this point in the history
  • Loading branch information
cagnulein committed Aug 17, 2023
1 parent a301585 commit 7d36b40
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 178 deletions.
217 changes: 107 additions & 110 deletions src/windows/zwift-incline-climb-portal.py
Original file line number Diff line number Diff line change
@@ -1,110 +1,107 @@
# iFit-Wolf3 - Autoincline control of treadmill via ADB and OCR
# Author: Al Udell
# Revised: April 22, 2023

# process-image.py - take Zwift screenshot, crop incline, OCR incline

# imports
import cv2
import numpy as np
import re
from datetime import datetime
from paddleocr import PaddleOCR
from PIL import Image, ImageGrab

# Take Zwift screenshot
screenshot = ImageGrab.grab()

# Scale image to 3000 x 2000
screenshot = screenshot.resize((3000, 2000))

# Convert screenshot to a numpy array
screenshot_np = np.array(screenshot)

# Crop image to incline area
screenwidth, screenheight = screenshot.size

# Values for Zwift climb portal incline
col1 = int(screenwidth/3000 * 2822)
row1 = int(screenheight/2000 * 218)
col2 = int(screenwidth/3000 * 2980)
row2 = int(screenheight/2000 * 302)

cropped_np = screenshot_np[row1:row2, col1:col2]

# Convert numpy array to PIL image
cropped_pil = Image.fromarray(cropped_np)

# Convert PIL Image to a cv2 image
cropped_cv2 = cv2.cvtColor(np.array(cropped_pil), cv2.COLOR_RGB2BGR)

# Convert cv2 image to HSV
result = cropped_cv2.copy()
image = cv2.cvtColor(cropped_cv2, cv2.COLOR_BGR2HSV)

# Isolate white mask
lower = np.array([0,0,159])
upper = np.array([0,0,255])
mask0 = cv2.inRange(image, lower, upper)
result0 = cv2.bitwise_and(result, result, mask=mask0)

# Isolate yellow mask
lower = np.array([24,239,241])
upper = np.array([24,253,255])
mask1 = cv2.inRange(image, lower, upper)
result1 = cv2.bitwise_and(result, result, mask=mask1)

# Isolate orange mask
lower = np.array([8,191,243])
upper = np.array([8,192,243])
mask2 = cv2.inRange(image, lower, upper)
result2 = cv2.bitwise_and(result, result, mask=mask2)

# Isolate red mask
lower = np.array([0,255,255])
upper = np.array([10,255,255])
mask3 = cv2.inRange(image, lower, upper)
result3 = cv2.bitwise_and(result, result, mask=mask3)

# Join colour masks
mask = mask0+mask1+mask2+mask3

# Set output image to zero everywhere except mask
merge = image.copy()
merge[np.where(mask==0)] = 0

# Convert to grayscale
gray = cv2.cvtColor(merge, cv2.COLOR_BGR2GRAY)

# Convert to black/white by threshold
ret,bin = cv2.threshold(gray,30,255,cv2.THRESH_BINARY)

# Closing
kernel = np.ones((3,3),np.uint8)
closing = cv2.morphologyEx(bin, cv2.MORPH_CLOSE, kernel)

# Invert black/white
inv = cv2.bitwise_not(closing)

# Apply average blur
averageBlur = cv2.blur(inv, (3, 3))

# OCR image
ocr = PaddleOCR(lang='en', use_gpu=False, enable_mkldnn=True, use_angle_cls=False, table=False, layout=False, show_log=False)
result = ocr.ocr(averageBlur, cls=False, det=True, rec=True)

# Extract OCR text
ocr_text = ''
for line in result:
for word in line:
ocr_text += f"{word[1][0]}"

# Remove all characters that are not "-" and integers from OCR text
pattern = r"[^-\d]+"
ocr_text = re.sub(pattern, "", ocr_text)
if ocr_text:
incline = ocr_text
else:
incline = 'None'

print(incline)
# iFit-Wolf3 - Autoincline control of treadmill via ADB and OCR
# Author: Al Udell
# Revised: August 16, 2023

# zwift-incline-climb-portal.py - take Zwift screenshot, crop incline, OCR incline

# imports
import cv2
import numpy as np
import re
from datetime import datetime
from paddleocr import PaddleOCR
from PIL import Image, ImageGrab

# Take Zwift screenshot
screenshot = ImageGrab.grab()

# Scale image to 3000 x 2000
screenshot = screenshot.resize((3000, 2000))

# Crop image to incline area
screenwidth, screenheight = screenshot.size

# Values for Zwift climb portal incline
col1 = int(screenwidth/3000 * 2822)
row1 = int(screenheight/2000 * 218)
col2 = int(screenwidth/3000 * 2980)
row2 = int(screenheight/2000 * 302)

cropped = screenshot.crop((col1, row1, col2, row2))

# Scale image to correct size for borderless window mode
width, height = cropped.size
cropped = cropped.resize((int(width * 1.3), int(height * 1.3)))

# Convert image to np array
cropped_np = np.array(cropped)

# Convert np array to PIL
cropped_pil = Image.fromarray(cropped_np)

# Convert PIL image to cv2 RGB
cropped_cv2 = cv2.cvtColor(np.array(cropped_pil), cv2.COLOR_RGB2BGR)

# Convert cv2 RGB to HSV
result = cropped_cv2.copy()
image = cv2.cvtColor(cropped_cv2, cv2.COLOR_BGR2HSV)

# Isolate white mask
lower = np.array([0,0,159])
upper = np.array([0,0,255])
mask0 = cv2.inRange(image, lower, upper)
result0 = cv2.bitwise_and(result, result, mask=mask0)

# Isolate yellow mask
lower = np.array([24,239,241])
upper = np.array([24,253,255])
mask1 = cv2.inRange(image, lower, upper)
result1 = cv2.bitwise_and(result, result, mask=mask1)

# Isolate orange mask
lower = np.array([8,191,243])
upper = np.array([8,192,243])
mask2 = cv2.inRange(image, lower, upper)
result2 = cv2.bitwise_and(result, result, mask=mask2)

# Isolate red mask
lower = np.array([0,255,255])
upper = np.array([10,255,255])
mask3 = cv2.inRange(image, lower, upper)
result3 = cv2.bitwise_and(result, result, mask=mask3)

# Join colour masks
mask = mask0+mask1+mask2+mask3

# Set output image to zero everywhere except mask
merge = image.copy()
merge[np.where(mask==0)] = 0

# Convert to grayscale
gray = cv2.cvtColor(merge, cv2.COLOR_BGR2GRAY)

# Convert to black/white by threshold
ret,bin = cv2.threshold(gray, 70, 255, cv2.THRESH_BINARY_INV)

# Apply gaussian blur
gaussianBlur = cv2.GaussianBlur(bin,(3,3),0)

# OCR image
ocr = PaddleOCR(lang='en', use_gpu=False, enable_mkldnn=True, use_angle_cls=False, table=False, layout=False, show_log=False)
result = ocr.ocr(gaussianBlur, cls=False, det=True, rec=True)

# Extract OCR text
ocr_text = ''
for line in result:
for word in line:
ocr_text += f"{word[1][0]}"

# Remove all characters that are not "-" and integers from OCR text
pattern = r"[^-\d]+"
ocr_text = re.sub(pattern, "", ocr_text)
if ocr_text:
incline = ocr_text
else:
incline = 'None'

print(incline)
2 changes: 1 addition & 1 deletion src/windows/zwift-incline.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# iFit-Wolf3 - Autoincline control of treadmill via ADB and OCR
# Author: Al Udell
# Revised: August 14, 2023
# Revised: August 16, 2023

# zwift-incline.py - take Zwift screenshot, crop incline, OCR incline

Expand Down
138 changes: 71 additions & 67 deletions src/windows/zwift-workout.py
Original file line number Diff line number Diff line change
@@ -1,67 +1,71 @@
# iFit-Workout - Auto-incline and auto-speed control of treadmill via ADB and OCR for Zwift workouts
# Author: Al Udell
# Revised: April 27, 2023

# process-image.py - take Zwift screenshot, crop speed/incline instruction, OCR speed/incline

# imports
import cv2
import numpy as np
import re
from datetime import datetime
from paddleocr import PaddleOCR
from PIL import Image, ImageGrab

# Take Zwift screenshot
screenshot = ImageGrab.grab()

# Scale image to 3000 x 2000
screenshot = screenshot.resize((3000, 2000))

# Convert screenshot to a numpy array
screenshot_np = np.array(screenshot)

# Convert numpy array to a cv2 RGB image
screenshot_cv2 = cv2.cvtColor(screenshot_np, cv2.COLOR_BGR2RGB)

# Crop image to workout instruction area
screenwidth, screenheight = screenshot.size
col1 = int(screenwidth/3000 * 1010)
row1 = int(screenheight/2000 * 260)
col2 = int(screenwidth/3000 * 1285)
row2 = int(screenheight/2000 * 480)
cropped_cv2 = screenshot_cv2[row1:row2, col1:col2]

# OCR image
ocr = PaddleOCR(lang='en', use_gpu=False, enable_mkldnn=True, use_angle_cls=False, table=False, layout=False, show_log=False)
result = ocr.ocr(cropped_cv2, cls=False, det=True, rec=True)

# Extract OCR text
ocr_text = ''
for line in result:
for word in line:
ocr_text += f"{word[1][0]} "

# Find the speed number
num_pattern = r'\d+(\.\d+)?' # Regular expression pattern to match numbers with optional decimal places
unit_pattern = r'\s+(kph|mph)' # Regular expression pattern to match "kph" or "mph" units
speed_match = re.search(num_pattern + unit_pattern, ocr_text)
if speed_match:
speed = speed_match.group(0)
pattern = r'\d+\.\d+'
speed = re.findall(pattern, speed)[0]
else:
speed = 'None'

# Find the incline number
incline_pattern = r'\d+\s*%' # Regular expression pattern to match numbers with "%"
incline_match = re.search(incline_pattern, ocr_text)
if incline_match:
incline = incline_match.group(0)
pattern = r'\d+'
incline = re.findall(pattern, incline)[0]
else:
incline = 'None'

print(f"{speed};{incline}")

# iFit-Workout - Auto-incline and auto-speed control of treadmill via ADB and OCR for Zwift workouts
# Author: Al Udell
# Revised: August 16, 2023

# zwift-workout.py - take Zwift screenshot, crop speed/incline instruction, OCR speed/incline

# imports
import cv2
import numpy as np
import re
from datetime import datetime
from paddleocr import PaddleOCR
from PIL import Image, ImageGrab

# Take Zwift screenshot
screenshot = ImageGrab.grab()

# Scale image to 3000 x 2000
screenshot = screenshot.resize((3000, 2000))

# Crop image to workout instruction area
screenwidth, screenheight = screenshot.size

# Values for Zwift workout instructions
col1 = int(screenwidth/3000 * 1010)
row1 = int(screenheight/2000 * 260)
col2 = int(screenwidth/3000 * 1285)
row2 = int(screenheight/2000 * 480)

cropped = screenshot.crop((col1, row1, col2, row2))

# Scale image to correct size for borderless window mode
width, height = cropped.size
cropped = cropped.resize((int(width * 0.99), int(height * 0.99)))

# Convert image to np array
cropped_np = np.array(cropped)

# OCR image
ocr = PaddleOCR(lang='en', use_gpu=False, enable_mkldnn=True, use_angle_cls=False, table=False, layout=False, show_log=False)
result = ocr.ocr(cropped_np, cls=False, det=True, rec=True)

# Extract OCR text
ocr_text = ''
for line in result:
for word in line:
ocr_text += f"{word[1][0]} "

# Find the speed number
num_pattern = r'\d+(\.\d+)?' # Regular expression pattern to match numbers with optional decimal places
unit_pattern = r'\s+(kph|mph)' # Regular expression pattern to match "kph" or "mph" units
speed_match = re.search(num_pattern + unit_pattern, ocr_text)
if speed_match:
speed = speed_match.group(0)
pattern = r'\d+\.\d+'
speed = re.findall(pattern, speed)[0]
else:
speed = 'None'

# Find the incline number
incline_pattern = r'-?\d+\s*%' # Regular expression pattern to match numbers with "%"
incline_match = re.search(incline_pattern, ocr_text)
if incline_match:
incline = incline_match.group(0)
pattern = r'-?\d+'
incline = re.findall(pattern, incline)[0]
else:
incline = 'None'

print(f"{speed};{incline}")

0 comments on commit 7d36b40

Please sign in to comment.