From 1bec69aba4b6572993e6ee252c0294ca63728c27 Mon Sep 17 00:00:00 2001
From: Pablo <96899520+VA3HDL@users.noreply.github.com>
Date: Wed, 1 Feb 2023 21:48:35 -0500
Subject: [PATCH] New features and bug fixes
Added ability to choose .ini file at startup.
Added more settings on .ini file and on the GUI
---
config.ini | 22 ++++----
config_hdcam.ini | 37 +++++++++++++
config_logi_c920.ini | 37 +++++++++++++
config_logi_exp.ini | 37 +++++++++++++
pyRadioOCR.py | 125 +++++++++++++++++++++++++++++--------------
5 files changed, 207 insertions(+), 51 deletions(-)
create mode 100644 config_hdcam.ini
create mode 100644 config_logi_c920.ini
create mode 100644 config_logi_exp.ini
diff --git a/config.ini b/config.ini
index 13fd65d..c6f5eaf 100644
--- a/config.ini
+++ b/config.ini
@@ -1,27 +1,29 @@
[INTEGRATION]
commander = False
-log4om = True
+log4om = False
[OCR]
language = train
whitelist = .1234567890
-debug = True
+debug = False
+confidence = 40
+framedelta = 0.036
[CAMERA]
-device = 2
-frame_width = 352
-frame_height = 288
-brightness = 6
+device = 1
+frame_width = 640
+frame_height = 480
+brightness = 100
exposure = 0
[PREPROCESS]
-brightness = 61
+brightness = 105
slant = 0.95
-threshold = 252
+threshold = 197
autoroi = False
invert = True
scale = 200
-rotate180 = True
+rotate180 = False
[POSTPROCESS]
strip = , .
@@ -29,7 +31,7 @@ nodecimal = False
[VOICE]
enabled = False
-volume = 0.25
+volume = 0.5
rate = 125
welcome = Welcome to py Radio O C R
diff --git a/config_hdcam.ini b/config_hdcam.ini
new file mode 100644
index 0000000..f0143a4
--- /dev/null
+++ b/config_hdcam.ini
@@ -0,0 +1,37 @@
+[INTEGRATION]
+commander = False
+log4om = False
+
+[OCR]
+language = train
+whitelist = .1234567890
+debug = False
+confidence = 40
+framedelta = 0.3
+
+[CAMERA]
+device = 2
+frame_width = 640
+frame_height = 480
+brightness = 120
+exposure = 0
+
+[PREPROCESS]
+brightness = 0
+slant = 1.0
+threshold = 103
+autoroi = False
+invert = False
+scale = 100
+rotate180 = False
+
+[POSTPROCESS]
+strip = , .
+nodecimal = False
+
+[VOICE]
+enabled = False
+volume = 1.0
+rate = 125
+welcome = Welcome to py Radio O C R
+
diff --git a/config_logi_c920.ini b/config_logi_c920.ini
new file mode 100644
index 0000000..c6f5eaf
--- /dev/null
+++ b/config_logi_c920.ini
@@ -0,0 +1,37 @@
+[INTEGRATION]
+commander = False
+log4om = False
+
+[OCR]
+language = train
+whitelist = .1234567890
+debug = False
+confidence = 40
+framedelta = 0.036
+
+[CAMERA]
+device = 1
+frame_width = 640
+frame_height = 480
+brightness = 100
+exposure = 0
+
+[PREPROCESS]
+brightness = 105
+slant = 0.95
+threshold = 197
+autoroi = False
+invert = True
+scale = 200
+rotate180 = False
+
+[POSTPROCESS]
+strip = , .
+nodecimal = False
+
+[VOICE]
+enabled = False
+volume = 0.5
+rate = 125
+welcome = Welcome to py Radio O C R
+
diff --git a/config_logi_exp.ini b/config_logi_exp.ini
new file mode 100644
index 0000000..665a501
--- /dev/null
+++ b/config_logi_exp.ini
@@ -0,0 +1,37 @@
+[INTEGRATION]
+commander = False
+log4om = False
+
+[OCR]
+language = train
+whitelist = .1234567890
+debug = True
+confidence = 40
+framedelta = 0.3
+
+[CAMERA]
+device = 0
+frame_width = 352
+frame_height = 288
+brightness = 5
+exposure = 0
+
+[PREPROCESS]
+brightness = 61
+slant = 0.95
+threshold = 252
+autoroi = False
+invert = True
+scale = 200
+rotate180 = True
+
+[POSTPROCESS]
+strip = , .
+nodecimal = False
+
+[VOICE]
+enabled = False
+volume = 1.0
+rate = 125
+welcome = Welcome to py Radio O C R
+
diff --git a/pyRadioOCR.py b/pyRadioOCR.py
index c4bf8da..84bdf07 100644
--- a/pyRadioOCR.py
+++ b/pyRadioOCR.py
@@ -39,7 +39,9 @@
17. CAP_PROP_WHITE_BALANCE Currently unsupported
18. CAP_PROP_RECTIFICATION Rectification flag for stereo cameras (note: only supported by DC1394 v 2.x backend currently)
"""
+
from tkinter import *
+from tkinter.filedialog import askopenfile
from PIL import Image, ImageTk
import cv2
import numpy as np
@@ -141,9 +143,18 @@ def increase_brightness(img, value=30):
img = cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
return img
+# Initialize the GUI ------------------------------------------------------------------------------------------
+root = Tk()
+root.geometry("850x600")
+root.title("pyRadioOCR v0.8.1a by VA3HDL")
+
+iniFile = askopenfile(mode='r')
+
# Read the configuration
config = configparser.ConfigParser()
-config.read("./config.ini")
+config.read(iniFile.name)
+
+root.title("pyRadioOCR v0.8.1a by VA3HDL - Config: " + iniFile.name)
# Initialize the Voice engine
synthesizer = pyttsx3.init()
@@ -154,11 +165,6 @@ def increase_brightness(img, value=30):
synthesizer.runAndWait()
synthesizer.stop()
-# Initialize the GUI ------------------------------------------------------------------------------------------
-root = Tk()
-root.geometry("850x600")
-root.title("pyRadioOCR v0.8.1a by VA3HDL")
-
right_frame = Frame(root, width=200, height=480, bg='grey')
right_frame.pack(side='right', fill='both', padx=5, pady=5, expand=True, anchor=E)
@@ -178,59 +184,49 @@ def increase_brightness(img, value=30):
def on_select(v):
print(v)
if not debugMode.get():
- cv2.destroyWindow("Webcam")
- cv2.destroyWindow("Gray")
- cv2.destroyWindow("Thresh")
- cv2.destroyWindow('ROI')
- cv2.destroyWindow("Resized")
+ cv2.destroyAllWindows()
+ canvas.delete("all")
# Checkboxes
snd2com = BooleanVar()
snd2com.set(config.getboolean("INTEGRATION", "commander"))
c1 = Checkbutton(lft_col1, text="Commander", variable=snd2com, command=lambda: on_select(snd2com.get()))
-#c1.pack(anchor=NW)
+
c1.pack(side=TOP, fill=NONE, expand=FALSE, anchor=NW)
snd2log = BooleanVar()
snd2log.set(config.getboolean("INTEGRATION", "log4om"))
c2 = Checkbutton(lft_col1, text="Log4OM", variable=snd2log, command=lambda: on_select(snd2log.get()))
-#c2.pack(anchor=NW)
c2.pack(side=TOP, fill=NONE, expand=FALSE, anchor=NW)
voice = BooleanVar()
voice.set(config.getboolean("VOICE", "enabled"))
c3 = Checkbutton(lft_col1, text="Voice", variable=voice, command=lambda: on_select(voice.get()))
-#c3.pack(anchor=NW)
c3.pack(side=TOP, fill=NONE, expand=FALSE, anchor=NW)
autoROI = BooleanVar()
autoROI.set(config.getboolean("PREPROCESS", "autoROI"))
c4 = Checkbutton(lft_col1, text="Auto ROI", variable=autoROI, command=lambda: on_select(autoROI.get()))
-#c4.pack(anchor=NW)
c4.pack(side=TOP, fill=NONE, expand=FALSE, anchor=NW)
invert = BooleanVar()
invert.set(config.getboolean("PREPROCESS", "invert"))
c5 = Checkbutton(lft_col2, text="Invert", variable=invert, command=lambda: on_select(invert.get()))
-#c5.pack(anchor=NE)
c5.pack(side=TOP, fill=NONE, expand=FALSE, anchor=NW)
noDecimal = BooleanVar()
noDecimal.set(config.getboolean("POSTPROCESS", "noDecimal"))
c6 = Checkbutton(lft_col2, text="No decimals", variable=noDecimal, command=lambda: on_select(noDecimal.get()))
-#c6.pack(anchor=NE)
c6.pack(side=TOP, fill=NONE, expand=FALSE, anchor=NW)
debugMode = BooleanVar()
debugMode.set(config.getboolean("OCR", "debug"))
c7 = Checkbutton(lft_col2, text="Debug mode", variable=debugMode, command=lambda: on_select(debugMode.get()))
-#c7.pack(anchor=NE)
c7.pack(side=TOP, fill=NONE, expand=FALSE, anchor=NW)
rotate180 = BooleanVar()
rotate180.set(config.getboolean("PREPROCESS", "rotate180"))
c8 = Checkbutton(lft_col2, text="Rotate 180 deg", variable=rotate180, command=lambda: on_select(rotate180.get()))
-#c8.pack(anchor=NE)
c8.pack(side=TOP, fill=NONE, expand=FALSE, anchor=NW)
# Sliders
@@ -238,7 +234,7 @@ def on_select(v):
brightCam.pack(anchor=NE)
brightCam.set(config.get("CAMERA", "brightness"))
-exposureSet = Scale(right_frame, from_=0, to=-14, length=200, orient='horizontal', label="Exposure")
+exposureSet = Scale(right_frame, from_=0, to=-14, length=200, orient='horizontal', label="Cam Exposure")
exposureSet.pack(anchor=NE)
exposureSet.set(config.get("CAMERA", "exposure"))
@@ -250,14 +246,26 @@ def on_select(v):
threshSet.pack(anchor=NE)
threshSet.set(config.get("PREPROCESS", "threshold"))
-brightSet = Scale(right_frame, from_=0, to=255, length=200, orient='horizontal', label="Preproc Brightness")
+brightSet = Scale(right_frame, from_=0, to=255, length=200, orient='horizontal', label="Brightness correction")
brightSet.pack(anchor=NE)
brightSet.set(config.get("PREPROCESS", "brightness"))
-scaleSet = Scale(right_frame, from_=0, to=500, length=200, orient='horizontal', label="Scale %")
+scaleSet = Scale(right_frame, from_=0, to=500, length=200, orient='horizontal', label="ROI Scale %")
scaleSet.pack(anchor=NE)
scaleSet.set(config.get("PREPROCESS", "scale"))
+confiden = Scale(right_frame, from_=0, to=100, length=200, orient='horizontal', label="Confidence %")
+confiden.pack(anchor=NE)
+confiden.set(config.get("OCR", "confidence"))
+
+frmDelta = Scale(right_frame, from_= 0.00, to= 1.00, digits = 4, resolution = 0.001, length=200, orient='horizontal', label="Frame Delta %")
+frmDelta.pack(anchor=NE)
+frmDelta.set(config.get("OCR", "framedelta"))
+
+volume = Scale(right_frame, from_= 0.00, to= 1.00, digits = 3, resolution = 0.01, length=200, orient='horizontal', label="Voice Volume")
+volume.pack(anchor=NE)
+volume.set(config.get("VOICE", "volume"))
+
# /GUI ------------------------------------------------------------------------------------------
# Main code loop---------------------------------------------------------------------------------
@@ -295,9 +303,7 @@ def update():
# OCR code ------------------------------------------------------------------------------------------
img = frame
height, width, _ = img.shape
-
- #img = img[200:height-200, 250:width-200]
-
+
if debugMode.get():
cv2.imshow("Webcam", img)
@@ -389,12 +395,10 @@ def update():
minY = 0
maxY = 0
height, width = thresh.shape
- # roi = thresh
+
for i in range(len(contours)):
- x, y, w, h = cv2.boundingRect(contours_poly[i])
- # if h > 40 and w > 10: # 320x160
- if h > 22 and w > 10 and w < 20:
- #counter += 1
+ x, y, w, h = cv2.boundingRect(contours_poly[i])
+ if h > 22 and w > 10 and w < 20:
cv2.drawContours(drawing, contours_poly, i, (0, 255, 0))
cv2.rectangle(drawing, (x, y), (x+w, y+h), (0, 0, 255), 2)
minX = min(minX, x)
@@ -432,11 +436,11 @@ def update():
d = pytesseract.image_to_data(roi, config=custom_oem, output_type=Output.DICT)
if debugMode.get():
- print(numbers)
- print(d['conf'])
+ print(numbers, end=" ")
+ print(d['conf'], end=" ")
n_boxes = len(d['text'])
for i in range(n_boxes):
- if int(d['conf'][i]) > 40 and numbers.isnumeric():
+ if int(d['conf'][i]) > int(confiden.get()) and numbers.isnumeric():
(x, y, w, h) = (d['left'][i], d['top'][i], d['width'][i], d['height'][i])
roi = cv2.rectangle(roi, (x, y), (x + w, y + h), (0, 255, 0), 2)
if noDecimal.get():
@@ -446,18 +450,18 @@ def update():
fnumbers = numbers[:len(numbers)-1] + "." + numbers[-1:]
log4omDec = "00"
if debugMode.get():
- print(fnumbers)
+ print(fnumbers, end=" ")
if float(fnumbers) > 460000.0 or float(fnumbers) < 520.0:
maxX = 0 # Out of valid range ignore and reset the ROI
else:
valid, band, mode = checkFreq(float(fnumbers))
- if new_numbers != fnumbers and valid and percentage > 0.33:
+ if (new_numbers != fnumbers) and valid and (percentage > frmDelta.get()):
new_numbers = fnumbers
new_band = band
new_mode = mode
tcp_numbers = fnumbers + "0"
if debugMode.get():
- print( d['conf'], new_numbers, tcp_numbers)
+ print(d['conf'], new_numbers, tcp_numbers,end=" ")
if snd2com.get():
send2commander("CmdSetFreq", "xcvrfreq", tcp_numbers)
send2commander("CmdSetMode", "1", mode)
@@ -465,6 +469,7 @@ def update():
send2log4om( "SetTxFrequency", "" + numbers + log4omDec + "")
send2log4om( "SetMode", "" + mode + "")
if voice.get():
+ synthesizer.setProperty("volume", volume.get())
synthesizer.say(new_numbers + " kilohertz")
synthesizer.runAndWait()
synthesizer.stop()
@@ -498,12 +503,40 @@ def update():
# /OCR code ------------------------------------------------------------------------------------------
if rotate180.get():
frame = cv2.rotate(frame, cv2.ROTATE_180)
+
cv2.putText(frame, new_numbers + " kHz " + new_band + " " + new_mode, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 0, 255), 2)
+
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
photo = ImageTk.PhotoImage(image=Image.fromarray(frame))
canvas.create_image(0, 0, image=photo, anchor=NW)
canvas.imgtk = photo
-
+
+ if debugMode.get():
+ options = ">>> Editable parameters from GUI: "
+ options += "\nVOICE enabled........: " + str(voice.get())
+ options += "\nVOICE Volume.........: " + str(volume.get())
+ options += "\nINTEGRATION commander: " + str(snd2com.get())
+ options += "\nINTEGRATION log4om...: " + str(snd2log.get())
+ options += "\nPREPROCESS autoROI...: " + str(autoROI.get())
+ options += "\nPREPROCESS invert....: " + str(invert.get())
+ options += "\nPOSTPROCESS noDecimal: " + str(noDecimal.get())
+ options += "\nOCR debug............: " + str(debugMode.get())
+ options += "\nOCR confidence.......: " + str(confiden.get())
+ options += "\nOCR framedelta.......: " + str(frmDelta.get())
+ options += "\nCAMERA brightness....: " + str(brightCam.get())
+ options += "\nCAMERA exposure......: " + str(exposureSet.get())
+ options += "\nPREPROCESS slant.....: " + str(slantSet.get())
+ options += "\nPREPROCESS threshold.: " + str(threshSet.get())
+ options += "\nPREPROCESS brightness: " + str(brightSet.get())
+ options += "\nPREPROCESS scale.....: " + str(scaleSet.get())
+ options += "\nPREPROCESS rotate180.: " + str(rotate180.get())
+
+ y0, dy = 480-18*18, 18
+ for i, line in enumerate(options.split('\n')):
+ y = y0 + i*dy
+ # cv2.putText(frame, line, (5, y), cv2.FONT_HERSHEY_PLAIN, 1.00, (0, 0, 255), 1)
+ canvas.create_text(15, y, text=line, fill="white", font=('Consolas 8'), anchor=NW)
+
root.after(20,update)
# End of Main code loop---------------------------------------------------------------------------------
@@ -542,9 +575,11 @@ def update():
# create white canvass
scaleInt = int(config.getint("PREPROCESS", "scale")/100)
-whitebkg = np.zeros([config.getint("CAMERA", "frame_height")*scaleInt,
- config.getint("CAMERA", "frame_width")*scaleInt],
- dtype=np.uint8)
+#whitebkg = np.zeros([config.getint("CAMERA", "frame_height")*scaleInt,
+# config.getint("CAMERA", "frame_width")*scaleInt],
+# dtype=np.uint8)
+
+whitebkg = np.zeros([200,320],dtype=np.uint8)
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
@@ -562,15 +597,23 @@ def key_pressed(event):
ROIset = False
ready2set = True
autoROI.set(False)
+ elif event.char == 'a':
+ synthesizer.setProperty("volume", volume.get())
+ synthesizer.say(new_numbers + " kilohertz")
+ synthesizer.runAndWait()
+ synthesizer.stop()
elif event.char == 'q' or ord(event.char) == 27:
# Stop the program if the 'q' key is pressed
config.set("VOICE", "enabled", str(voice.get()))
+ config.set("VOICE", "volume", str(volume.get()))
config.set("INTEGRATION", "commander", str(snd2com.get()))
config.set("INTEGRATION", "log4om", str(snd2log.get()))
config.set("PREPROCESS", "autoROI", str(autoROI.get()))
config.set("PREPROCESS", "invert", str(invert.get()))
config.set("POSTPROCESS", "noDecimal", str(noDecimal.get()))
config.set("OCR", "debug", str(debugMode.get()))
+ config.set("OCR", "confidence", str(confiden.get()))
+ config.set("OCR", "framedelta", str(frmDelta.get()))
config.set("CAMERA", "brightness", str(brightCam.get()))
config.set("CAMERA", "exposure", str(exposureSet.get()))
config.set("PREPROCESS", "slant", str(slantSet.get()))
@@ -579,7 +622,7 @@ def key_pressed(event):
config.set("PREPROCESS", "scale", str(scaleSet.get()))
config.set("PREPROCESS", "rotate180", str(rotate180.get()))
- with open('./config.ini', 'w') as configfile:
+ with open(iniFile.name, 'w') as configfile:
config.write(configfile)
if debugMode.get():
print("Event - Quit...")