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...")