さらば、すべてのエヴァンゲリオン

我的Windows不可能这么的卡顿

Bug 发现

前些天,在bilibili的首页刷到一个名为《修BUG的第一天 我给微软的 PowerToys 修了一个 BUG》的视频。恰好Powertoys这款软件我也在使用,于是便点进去看了看视频,是一个文字识别输出方面的Bug,但视频作者过于自信的语气,加之“标题的第一天”就是没有C#或者大型项目开发经验的代名词,对于他代码,我怀疑有大问题。

第二天早上起来,测试相关模块功能,果然,只是从一个错误转移成了另一个错误。

Bug 解释

Using TextExtractor to extract Chinese always contains spaces

最初的bug源于此,是由于中文的文字图片识别引擎返回的内容是以为单位的,英文部分则是以词为单位。最初的代码在所有的识别单位中添加一个 (空格)作为间隔。如此便导致了中文汉字间包含错误空格。

[TextExtractor] Add space between CJK words and non-CJK

错误修复示范:遍历判断单位是否CJK文字,如果是则不加空格,不是的话就前后各加一个空格。也就是从这 里 是 AO 的 blog , 你 好 ! 变成了这里是 AO 的 blog , 你好 !

而实际正确的应该是这里是AO的blog,你好!,英文和中文之间最好没有空格,标点也不应该有空格。这里就有一个新问题,标点怎么涵盖?既然是CJK,那么日语和韩语的情况呢?韩文引擎表现不同,非单字识别,测试日文如下:

1そ の 後 、 2 人 の 住 む 関東 は 本格的 に 梅雨入 り し 、 雨 の 日 の 午前 た け の さ さ や か な 交流 が は し ま る 。

显然,日语的假名并没有算到CJK words里。观察一下源代码:

 1var cjkRegex = new Regex(@"\p{IsCJKUnifiedIdeographs}");
 2foreach (OcrLine ocrLine in ocrResult.Lines){
 3	bool isBeginning = true;
 4	bool isCJKPrev = false;
 5	foreach (OcrWord ocrWord in ocrLine.Words){
 6    	bool isCJK = cjkRegex.IsMatch(ocrWord.Text);
 7    	// Use spaces to separate non-CJK words.
 8    	if (!isBeginning && (!isCJK || !isCJKPrev)){
 9    	    _ = text.Append(' ');
10 	   }
11 	   _ = text.Append(ocrWord.Text);
12 	   isCJKPrev = isCJK;
13  	  isBeginning = false;
14}
15...
16}

问题就出现在代码最开头,CJKUnifiedIdeographs只包含CJK的汉字部分。因此数字,假名,符号,统统都有空格。

Patch

那么,是否把所有的CJK符号放到判断式子里就万事大吉了呢?并不,考虑下面情况,空格依旧是重要的:

1韩文半角符号
2⑨ 记得给你的switch充电
3「卍 解」天锁斩月
4现在的B站都喜欢给标题加上空格——《反 复 横 跳》

文本的多样性决定了,空格的出现是难以预料的,引擎的输出格式决定了丢失了一部分的信息,这部分的信息是难以通过代码复现的(比如这个项目的处理也有问题)。此问题应该交由引擎处理,或者分割开来,交给各语言的native speaker来写处理规则,而不是简单的加个残缺的范围了事。

考虑到这个模块也不是主要模块,引擎目前功能并不完善,不支持多语言识别,语言种类识别加之识别率一般,所以简单做个补丁。

1// var cjkRegex = new Regex(@"\p{IsCJKUnifiedIdeographs}|\p{IsHiragana}|\p{IsKatakana}|[\uFF61-\uFF9F]|[\u3000-\u3003]");
2var cjkRegex = new Regex(@"\p{IsCJKUnifiedIdeographs}|\p{IsHiragana}|\p{IsKatakana}|[\uFF61-\uFF9F]");

{IsCJKUnifiedIdeographs} for Kanji in JP and ZH {IsHiragana} for Hiragana {IsKatakana} for Katakana [\uFF61-\uFF9F] for Half-width Katakana [\u3000-\u3003] for the most common Punctuation

考虑中文、日文、韩文,加入最常用的假名,半角假名。可供正常阅读。

1その後 、 2 人の住む関東は本格的に梅雨入りし 、 雨の日の午前たけのささやかな交流がはじまる 。 

吐槽

  1. 这个问题并没有得到很好的解决。最上策:有一个完备的引擎处理,准确的识别,具备以行为单位输出的功能。中策:分开处理,对每一种语言的符号进行详细的考证,编写规则,确保一般文本的准确性。(混合语言和新型新式可能会出错。)

  2. 修复BUG的时候要考虑周全,描述简要突出。微软的审核者会通过最初的提交者(视频作者)的玩具代码,原因是BUG牵扯到语言相关的内容,审核者说明自己不是CJK语言使用者,不太懂之后,征求提交者的建议,视频作者一句LGTM (looks good to me),显然,面对80k的项目的责任,他自己却根本没仔细看,只顾着说“看我给微软修了个BUG”。拜托,你是新交了一个BUG,我人麻了,想到自己电脑里跑的这都是些啥程序。。。

  3. 微软,提高代码和审核的标准!(我说我电脑怎么越来越卡。这代码不卡才怪。) 如果没更好的解决办法,你就先把我的patch并进去或者把那个问题代码迁出来吧。

更新

上游程序通过更加复杂的筛选方案,已经修复此Bug,并已合并到Powertoy中,此帖终结。