diff --git a/gui.py b/gui.py new file mode 100644 index 0000000..8b9e199 --- /dev/null +++ b/gui.py @@ -0,0 +1,126 @@ + +import tkinter as tk +from tkinter import ttk, filedialog, scrolledtext +import threading +import queue +import sys +import os + +# Add the project root to the Python path to allow importing jyakoTen +sys.path.insert(0, os.path.abspath(os.path.dirname(__file__))) + +from jyakoTen.score import run_scoring + +class QueueIO(queue.Queue): + def write(self, msg): + self.put(msg) + + def flush(self): + sys.__stdout__.flush() + +class App(tk.Tk): + def __init__(self): + super().__init__() + self.title("JyakoTen Scorer") + self.geometry("800x600") + + self.create_widgets() + + def create_widgets(self): + main_frame = ttk.Frame(self, padding="10") + main_frame.pack(fill=tk.BOTH, expand=True) + + # --- File Path Inputs --- + path_frame = ttk.LabelFrame(main_frame, text="Input Files", padding="10") + path_frame.pack(fill=tk.X, pady=5) + path_frame.columnconfigure(1, weight=1) + + # Transcript Path + ttk.Label(path_frame, text="Transcript Path:").grid(row=0, column=0, sticky=tk.W, padx=5, pady=5) + self.transcript_path = tk.StringVar() + transcript_entry = ttk.Entry(path_frame, textvariable=self.transcript_path) + transcript_entry.grid(row=0, column=1, sticky="ew", padx=5) + transcript_button = ttk.Button(path_frame, text="Browse...", command=lambda: self.browse_file(self.transcript_path)) + transcript_button.grid(row=0, column=2, padx=5) + + # Recognition Path + ttk.Label(path_frame, text="Recognition Path:").grid(row=1, column=0, sticky=tk.W, padx=5, pady=5) + self.recognition_path = tk.StringVar() + recognition_entry = ttk.Entry(path_frame, textvariable=self.recognition_path) + recognition_entry.grid(row=1, column=1, sticky="ew", padx=5) + recognition_button = ttk.Button(path_frame, text="Browse...", command=lambda: self.browse_file(self.recognition_path)) + recognition_button.grid(row=1, column=2, padx=5) + + # --- Execute Button --- + self.execute_button = ttk.Button(main_frame, text="Execute Scoring", command=self.execute_scoring) + self.execute_button.pack(pady=10) + + # --- Results Display --- + results_frame = ttk.LabelFrame(main_frame, text="Results", padding="10") + results_frame.pack(fill=tk.BOTH, expand=True) + + self.results_text = scrolledtext.ScrolledText(results_frame, wrap=tk.WORD, state="disabled") + self.results_text.pack(fill=tk.BOTH, expand=True) + + def browse_file(self, string_var): + filename = filedialog.askopenfilename() + if filename: + string_var.set(filename) + + def execute_scoring(self): + transcript_path = self.transcript_path.get() + recognition_path = self.recognition_path.get() + + if not transcript_path or not recognition_path: + self.update_results("Error: Both transcript and recognition paths must be selected.\n") + return + + self.execute_button.config(state="disabled") + self.update_results("Starting scoring process...\n", clear=True) + + args_dict = { + "transcript_path": transcript_path, + "recognition_path": recognition_path, + # Add other default arguments from score.py if necessary + 'key1': 'gui_run', + } + + self.process_queue = QueueIO() + threading.Thread(target=self.scoring_thread_target, args=(args_dict,), daemon=True).start() + self.after(100, self.check_queue) + + def scoring_thread_target(self, args_dict): + original_stdout = sys.stdout + sys.stdout = self.process_queue + + try: + run_scoring(args_dict) + except Exception as e: + self.process_queue.write(f"\n--- An error occurred ---\n{e}\n") + finally: + sys.stdout = original_stdout + self.process_queue.put(None) # Signal that the process is done + + def check_queue(self): + try: + line = self.process_queue.get_nowait() + if line is None: + self.execute_button.config(state="normal") + self.update_results("\nScoring process finished.\n") + else: + self.update_results(line) + self.after(100, self.check_queue) + except queue.Empty: + self.after(100, self.check_queue) + + def update_results(self, message, clear=False): + self.results_text.config(state="normal") + if clear: + self.results_text.delete(1.0, tk.END) + self.results_text.insert(tk.END, message) + self.results_text.see(tk.END) + self.results_text.config(state="disabled") + +if __name__ == "__main__": + app = App() + app.mainloop() diff --git a/jyakoTen/score.py b/jyakoTen/score.py index 81c86fd..66916a5 100644 --- a/jyakoTen/score.py +++ b/jyakoTen/score.py @@ -36,89 +36,35 @@ def detect_success_fail_words(word1,word2): return " ".join(result) - - -# arg parser -def score_main(): - parser = argparse.ArgumentParser(description='Score result') - parser.add_argument('--transcript_path',"-tp" , - help='correct text path') - parser.add_argument('--transcript_kanji_index', "-tki", - help='kanji',default=0,type=int) - parser.add_argument('--transcript_index', "-ti", - help='1 for kana',default=1,type=int) - parser.add_argument('--transcript_convert_kana',"-tc", - help='convert to kana',action='store_true') - parser.add_argument('--transcript_use_key',"-tk", - help='use transcript 0 value instead number',action='store_true') - - # TODO support - #parser.add_argument('--use_transcript_kanji', help='is transcript are kanji',action='store_true') - - parser.add_argument('--transcript_splitter',"-ts" , - help='split text',default=":") - - parser.add_argument('--min_score' , - help='print minscore',type = float, default=0) - - parser.add_argument('--recognition_path', "-rp", - help='correct text path') - parser.add_argument('--recognition_index', "-ri", - help='0 means flat',default=0,type=int) - parser.add_argument('--recognition_splitter',"-rs" , - help='split text',default="|") +def run_scoring(args_dict): + key1 = args_dict.get('key1') + key2 = args_dict.get('key2', '0000') + key3 = args_dict.get('key3', 'recitation') + key4 = args_dict.get('key4') + use_mecab = args_dict.get('no_mecab', True) + + transcript_path = args_dict.get('transcript_path') + transcript_index = args_dict.get('transcript_index', 1) + transcript_kanji_index = args_dict.get('transcript_kanji_index', 0) + transcript_splitter = args_dict.get('transcript_splitter', ':') + transcript_use_key = args_dict.get('transcript_use_key', False) + + recognition_path = args_dict.get('recognition_path') + recognition_index = args_dict.get('recognition_index', 0) + recognition_splitter = args_dict.get('recognition_splitter', '|') + + min_score = args_dict.get('min_score', 0) + key_dir = args_dict.get('key_dir') + use_mora = args_dict.get('use_mora', False) + sort_per = args_dict.get('sort_per', False) - - parser.add_argument('--key_dir', - help='file exist dir') - - parser.add_argument('--key1', - help='key1 cv01 or ita01..') - parser.add_argument('--key2', - help='key2 epoch number or voice changed cv02',default='0000') - - parser.add_argument('--key3', - help='key3 recitation or emotion or endsville400',default='recitation') - parser.add_argument('--key4', - help='optional int or float') - parser.add_argument('--no_mecab', - help='not use mecab',action='store_false') - parser.add_argument('--use_mora', - help='calcurate mora base',action='store_true') - parser.add_argument('--sort_per',"-sp", - help='calcurate mora base',action='store_true') - - - - args = parser.parse_args() - key1 = args.key1 - key2 = args.key2 - key3 = args.key3 - key4 = args.key4 - use_mecab = args.no_mecab - - transcript_path = args.transcript_path - transcript_index = args.transcript_index - transcript_kanji_index = args.transcript_kanji_index - transcript_splitter = args.transcript_splitter - - recognition_path = args.recognition_path - recognition_index = args.recognition_index - recognition_splitter = args.recognition_splitter - - min_score = args.min_score - # use openjtalk dic and replace text use_user_dic = False # check args - if args.recognition_path is None and args.key1 is None: + if recognition_path is None and key1 is None: print("Error: At least one of --recognition_path or --key1 must be specified.") - parser.print_help() - exit(1) - - - + return current_dir=os.path.dirname(__file__) @@ -126,7 +72,6 @@ def score_main(): pyopenjtalk.mecab_dict_index(current_dir+"/"+"user.csv", "user.dic") pyopenjtalk.update_global_jtalk_with_user_dict("user.dic") - emotion_path = "transcripts/emotion_transcript_utf8.txt" emotion_path = current_dir+"/transcripts/"+f"{key3}_transcript_utf8.txt" @@ -134,7 +79,6 @@ def score_main(): print(f"use transcript:{transcript_path} index = {transcript_index}") emotion_path = transcript_path - #transcript_kanjis = [] # TODO support kanji? transcript_kanas = [] transcript_keys = [] transcript_kanjis = [] @@ -160,29 +104,21 @@ def score_main(): transcript_kanas.append(kana) transcript_keys.append(id) transcript_map[id]={"kana":kana,"kanji":kanji} - #transcript_kanjis.append() - option_key="" if key4!= None: - option_key="_"+key4 - - + option_key="_"+str(key4) file_path = f"{key1}_{key2}_{key3}_result{option_key}.txt" - if args.key_dir!= None: - file_path = os.path.join(args.key_dir,file_path) + if key_dir!= None: + file_path = os.path.join(key_dir,file_path) else: file_path = os.path.join(os.getcwd(),file_path) - if recognition_path: print(f"file path({file_path}) replaced recognition_path({recognition_path})") file_path = recognition_path - - - index = 0 out = [] out.append(",".join(["index","kanji_cer","kana_cer","mora_er","phonome_er","transcript_kanji","transcript_kana","detected_kanji","detected_kana","best_kana","transcript_mora","detected_mora","diff_mora"])+"\n") @@ -190,6 +126,7 @@ def score_main(): total_score = 0 low_text = "" low_score = 1 + low_id = -1 case2 = 0 case3 = 0 total_kanji_cer =0 @@ -211,7 +148,7 @@ def score_main(): values = line.split(recognition_splitter) line = values[recognition_index] line = replace_chars(line) - if line == "ご視聴ありがとうございました" and args.transcript_use_key:#numbers need key + if line == "ご視聴ありがとうございました" and transcript_use_key: print("skip ご視聴ありがとうございました") skipped += 1 index += 1 @@ -226,8 +163,6 @@ def score_main(): if converted_dic["no_alakana_words"]: print(f"Converting English to Kan faild(no in dictionary) or skipped(short words) {converted_dic['no_alakana_words']}.most of case pronounce english is too bad.should add dictionary the wordss") - # TODO separate dic - # recitation 324 if key3 == "recitation": line = line.replace("1877","センハッピャクナナジュウナナ") @@ -238,16 +173,11 @@ def score_main(): line= line.replace("7-20","セブントゥウェンティ") line= line.replace("360","スリーシックスティ") - - - - - if args.transcript_use_key: + if transcript_use_key: index_text = transcript_keys[index] else: index_text = f"{index+1:03d}" - kanji = transcript_kanjis[index] kana = transcript_kanas[index] phones2 = pyopenjtalk.g2p(kana, kana=False) @@ -261,14 +191,14 @@ def score_main(): total_kanji_cer += kanji_cer total_kana_cer += kana_cer - high_score,high_score_text,high_moras = mecab_utils.get_best_group(line,kana,True,True,args.use_mora) - high_score2,high_score_text2,high_moras2 = mecab_utils.get_best_group(line,kana,False,True,args.use_mora) + high_score,high_score_text,high_moras = mecab_utils.get_best_group(line,kana,True,True,use_mora) + high_score2,high_score_text2,high_moras2 = mecab_utils.get_best_group(line,kana,False,True,use_mora) if high_score2 > high_score: high_score = high_score2 high_score_text = high_score_text2 high_moras = high_moras2 case2+=1 - high_score3,high_score_text3,high_moras3 = mecab_utils.get_best_group(line,kana,True,False,args.use_mora) + high_score3,high_score_text3,high_moras3 = mecab_utils.get_best_group(line,kana,True,False,use_mora) if high_score3 > high_score: is_case2 = high_score == high_score2 high_score = high_score3 @@ -278,9 +208,7 @@ def score_main(): if is_case2: case3-=1 - score = high_score - if score 20:#debug - # break - - max_score = index - # TODO add verbose mode - #print(f"no mecab:{case2},no group:{case3}") score = total_score for low in lows: print(low) @@ -325,25 +245,82 @@ def score_main(): if not use_mecab: option_key +="_no-mecab" - if args.use_mora: + if use_mora: option_key +="_use-mora" - average_kanji_cer=total_kanji_cer/(index) #I'M not sure why add +1 - average_kana_cer=total_kana_cer/(index) - average_mer =total_mer/(index) - average_per =total_per/(index) + if index > 0: + average_kanji_cer=total_kanji_cer/(index) + average_kana_cer=total_kana_cer/(index) + average_mer =total_mer/(index) + average_per =total_per/(index) + + print(f"{key1} {key2} MER:{average_mer}") + print(f"Lowest:id={low_id} max-error={(1.0-low_score):.3f}:detect={low_text} correct={low_correct}") + + output_file_name = f"{key1}_{key2}_{key3}_total-{max_score}-skipped-{skipped:02d}_cer-kanji-{average_kanji_cer:.5f}_kana-{average_kana_cer:.5f}_mer-{average_mer:.5f}_per-{average_per:.5f}{option_key}.txt" + output_path = os.path.join(os.getcwd(),output_file_name) + + if sort_per: + sorted_out =sorted(out[1:],key=lambda x: float(x.split(',')[4].strip()), reverse=True) + sorted_out.insert(0, out[0]) + out = sorted_out + + with open(output_path, 'w') as f: + f.writelines(out) + else: + print("No lines were processed.") + +# arg parser +def score_main(): + parser = argparse.ArgumentParser(description='Score result') + parser.add_argument('--transcript_path',"-tp" , + help='correct text path') + parser.add_argument('--transcript_kanji_index', "-tki", + help='kanji',default=0,type=int) + parser.add_argument('--transcript_index', "-ti", + help='1 for kana',default=1,type=int) + parser.add_argument('--transcript_convert_kana',"-tc", + help='convert to kana',action='store_true') + parser.add_argument('--transcript_use_key',"-tk", + help='use transcript 0 value instead number',action='store_true') + + # TODO support + #parser.add_argument('--use_transcript_kanji', help='is transcript are kanji',action='store_true') - print(f"{key1} {key2} MER:{average_mer}") - print(f"Lowest:id={low_id} max-error={(1.0-low_score):.3f}:detect={low_text} correct={low_correct}") + parser.add_argument('--transcript_splitter',"-ts" , + help='split text',default=":") + + parser.add_argument('--min_score' , + help='print minscore',type = float, default=0) + + parser.add_argument('--recognition_path', "-rp", + help='correct text path') + parser.add_argument('--recognition_index', "-ri", + help='0 means flat',default=0,type=int) + parser.add_argument('--recognition_splitter',"-rs" , + help='split text',default="|") - #print(total_mer,average_mer) - output_file_name = f"{key1}_{key2}_{key3}_total-{max_score}-skipped-{skipped:02d}_cer-kanji-{average_kanji_cer:.5f}_kana-{average_kana_cer:.5f}_mer-{average_mer:.5f}_per-{average_per:.5f}{option_key}.txt" - output_path = os.path.join(os.getcwd(),output_file_name) - if args.sort_per: - sorted_out =sorted(out[1:],key=lambda x: float(x.split(',')[4].strip()), reverse=True) - sorted_out.insert(0, out[0]) - out = sorted_out + parser.add_argument('--key_dir', + help='file exist dir') - with open(output_path, 'w') as f: - f.writelines(out) \ No newline at end of file + parser.add_argument('--key1', + help='key1 cv01 or ita01..') + parser.add_argument('--key2', + help='key2 epoch number or voice changed cv02',default='0000') + + parser.add_argument('--key3', + help='key3 recitation or emotion or endsville400',default='recitation') + parser.add_argument('--key4', + help='optional int or float') + parser.add_argument('--no_mecab', + help='not use mecab',action='store_false') + parser.add_argument('--use_mora', + help='calcurate mora base',action='store_true') + parser.add_argument('--sort_per',"-sp", + help='calcurate mora base',action='store_true') + + + + args = parser.parse_args() + run_scoring(vars(args))