其实如果是对算法的输出结果进行可视化的话,使用 Pillow 库是完全没有问题的。但是存在着这样一种情况,我们调用的公共包当中,里面已经有了可视化的接口,但是使用的是 OpenCV 中的 cv2.putText
进行可视化的。正常来说,因为 OpenCv 对中文的支持不足,直接调用的话,显示在图片上的 label 名肯定是一串问号,也就是乱码,这时,就有几个选择:
- 自己从零实现一个可视化的功能,使用 Pillow 或者别的库进行可视化
- pip 安装完 Sahi 库之后,手动进入到库内修改相关的代码,将
cv2.putText
改成 Pillow 相关的方法
第一个选择虽然不是不可行,但是重复造轮子确实挺浪费时间的,至于第二个选择,对于我来说存在一个问题,因为我这个算法是要打包成 whl 安装包的,我不可能每安装一次,就去手动修改一下 Sahi 的文件,或者是替换一下它的文件,所以也只能否决了。
但是我想到第三种方法:
- 使用 Monkey Patch 来替换 OpenCv 的
cv2.putText
函数
之前虽然看到过 Monkey Patch 这种用法,但是一直没有遇到什么相关的场景,对它的理解也是懵懵懂懂,觉得这玩意真的有用吗?今天终于让我遇到了比较合适的场景:
无需修改公有库的源文件,就可以修改其功能。
下面给出一个初版的代码,因为并没有将 cv2.putText
的全部功能都实现了,只是实现了些基础的特性:
import types
from typing import Optional, Sequenceimport cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFontfont_path = 'SimHei.ttf'def putText(self,img: np.ndarray,text: str,org: Sequence[int],fontFace: int,fontScale: Optional[float],color: Sequence[int],thickness: Optional[float] = 1,lineType=cv2.LINE_AA,bottomLeftOrigin=True,):base_size = 30fontSize = base_size * fontScaleimg_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))draw = ImageDraw.Draw(img_pil)fontStyle = ImageFont.truetype(font=font_path,size=int(fontSize),encoding='utf-8')draw.text(xy=(int(org[0]), int(org[1]-fontSize)),text=text,fill=color,font=fontStyle)img_cv2 = cv2.cvtColor(np.asarray(img_pil), cv2.COLOR_RGB2BGR)img[...] = img_cv2cv2.putText = types.MethodType(putText, None)
后续再使用 Sahi 进行可视化的时候,就可以正常显示中文了。
- 参数列表的
self
是需要的,可以替换为任意的参数名,因为 OpenCv 调用的时候第一个会传入一个隐藏的参数。 - SimHei.ttf 字体文件得自己找个地方下载一下,可以替换为别的字体
- 因为
cv2.putText
就是原图上绘制,没有返回值得,所以我也将没有用返回值,直接将绘制得结果又放回了img
简单的打了个小补丁,不考虑格式化的话,大概 10 左右就解决了 Sahi 中文乱码问题(虽然在大型项目中不推荐这么用,可能会考虑不周,导致可怕的后果,甚至更谨慎一点,可以将最后一行放入一个可视化的 if
语句内,只有收到可视化的指令,才使用 Monkey Patch 进行替换,将对其他可能用到 cv2.putText
函数的地方,影响降低到最小)