hiroi-sora / Umi-OCR

OCR software, free and offline. 开源、免费的离线OCR软件。支持截屏/批量导入图片,PDF文档识别,排除水印/页眉页脚,扫描/生成二维码。内置多国语言库。
MIT License
27.14k stars 2.72k forks source link

2k屏幕电脑,在使用HDMI接口外接显示屏之后,点击截图键会出现蓝色的颜色块,影响截图 #279

Closed lzh1043060917 closed 10 months ago

lzh1043060917 commented 10 months ago
Snipaste_2023-12-22_14-55-33

我的电脑是天选4,屏幕2k屏,在使用HDMI接口外接显示屏之后,点击截图键会出现蓝色的颜色块,导致无法正常截图;但是我换了个1080p屏幕的笔记本电脑,外接显示屏之后,点击截图键就没有这个问题了。两种版本的umi-ocr都出现了这样的问题,不知道有人知道啥原因么?

lzh1043060917 commented 10 months ago

我的天选4是win11系统,cpu是amd,屏幕刷新率较高,但是外接显示屏只有60hz,我不太清楚和这个有没有关系。另一台没问题的电脑屏幕是1080p,60hz刷新率,win10系统,cpu是英特尔,除此之外想不到有啥差别了......

lzh1043060917 commented 10 months ago

补充一下,我的2k屏的笔记本电脑,如果不外接显示器,直接用截图功能,就不会出现蓝色的块。只有在使用HDMI外接显示器之后才会出现这个情况。

hiroi-sora commented 10 months ago

试下关闭硬件加速

image

lzh1043060917 commented 10 months ago

试下关闭硬件加速

image

试了,没效果

hiroi-sora commented 10 months ago

请尝试下列操作:

  1. 打开 UmiOCR-data\py_src\image_controller\image_provider.py ,将30行的requestPixmap函数替换为:

    def requestPixmap(self, path, size=None, resSize=None):
        if "/" in path:
            compID, imgID = path.split("/", 1)
            self._delCompCache(compID)  # 先清缓存
            if imgID in self.pixmapDict:
                self.compDict[compID] = imgID  # 记录缓存
                return self.pixmapDict[imgID]
            print(f"[Error] imgID {imgID} not in pixmapDict")
        else:  # 清空一个组件的缓存
            self._delCompCache(path)
        return self._getNoneImg()  # 返回占位符
  2. 打开 UmiOCR-data\py_src\image_controller\screenshot_controller.py ,将第16行的getScreenshot函数替换为:

    def getScreenshot(self, wait=0):
        if wait > 0:
            time.sleep(wait)
        try:
            grabList = []
            screensList = QGuiApplication.screens()
            for screen in screensList:
                pixmap = screen.grabWindow(0)  # 截图
                imgID = PixmapProvider.addPixmap(pixmap)  # 存入提供器,获取imgID
                grabList.append(
                    {
                        "imgID": imgID,
                        "screenName": screen.name(),
                    }
                )
                print(imgID)
                print(screen.name())
                print(screen.devicePixelRatio())
                print(screen.virtualGeometry())
                print(screen.geometry())
            return grabList
        except Exception as e:
            return [f"[Error] Screenshot: {e}"]

然后,在命令行中运行Umi-OCR,也可以直接运行 UmiOCR-data\RUN_CLI.bat 。截图,如果出现异常颜色块的话,将命令行输出的内容粘上来给我看看。

lzh1043060917 commented 10 months ago

请尝试下列操作:

  1. 打开 UmiOCR-data\py_src\image_controller\image_provider.py ,将30行的requestPixmap函数替换为:
    def requestPixmap(self, path, size=None, resSize=None):
        if "/" in path:
            compID, imgID = path.split("/", 1)
            self._delCompCache(compID)  # 先清缓存
            if imgID in self.pixmapDict:
                self.compDict[compID] = imgID  # 记录缓存
                return self.pixmapDict[imgID]
            print(f"[Error] imgID {imgID} not in pixmapDict")
        else:  # 清空一个组件的缓存
            self._delCompCache(path)
        return self._getNoneImg()  # 返回占位符
  1. 打开 UmiOCR-data\py_src\image_controller\screenshot_controller.py ,将第16行的getScreenshot函数替换为:
    def getScreenshot(self, wait=0):
        if wait > 0:
            time.sleep(wait)
        try:
            grabList = []
            screensList = QGuiApplication.screens()
            for screen in screensList:
                pixmap = screen.grabWindow(0)  # 截图
                imgID = PixmapProvider.addPixmap(pixmap)  # 存入提供器,获取imgID
                grabList.append(
                    {
                        "imgID": imgID,
                        "screenName": screen.name(),
                    }
                )
                print(imgID)
                print(screen.name())
                print(screen.devicePixelRatio())
                print(screen.virtualGeometry())
                print(screen.geometry())
            return grabList
        except Exception as e:
            return [f"[Error] Screenshot: {e}"]

然后,在命令行中运行Umi-OCR,也可以直接运行 UmiOCR-data\RUN_CLI.bat 。截图,如果出现异常颜色块的话,将命令行输出的内容粘上来给我看看。

Snipaste_2023-12-29_09-37-11
hiroi-sora commented 10 months ago

啊??太玄学了,截图对象已经缓存到字典里了怎么会莫名其妙的消失了呢……

麻烦再改一下,UmiOCR-data\py_src\image_controller\image_provider.py

requestPixmap函数改为:

    def requestPixmap(self, path, size=None, resSize=None):
        print(f"request: {path}")
        print("   ", list(k[-6:] for k in self.pixmapDict.keys()))
        if "/" in path:
            compID, imgID = path.split("/", 1)
            self._delCompCache(compID)  # 先清缓存
            if imgID in self.pixmapDict:
                self.compDict[compID] = imgID  # 记录缓存
                return self.pixmapDict[imgID]
            print(f"[Error] imgID {imgID} not in pixmapDict!")
        else:  # 清空一个组件的缓存
            self._delCompCache(path)
        return self._getNoneImg()  # 返回占位符

后面的 _delCompCache 函数改为:

    def _delCompCache(self, compID):
        if compID in self.compDict:
            last = self.compDict[compID]
            if last in self.pixmapDict:
                print(f"___del: {compID} {last}")
                del self.pixmapDict[last]
            del self.compDict[compID]
lzh1043060917 commented 10 months ago

啊??太玄学了,截图对象已经缓存到字典里了怎么会莫名其妙的消失了呢……

麻烦再改一下,UmiOCR-data\py_src\image_controller\image_provider.py

requestPixmap函数改为:

    def requestPixmap(self, path, size=None, resSize=None):
        print(f"request: {path}")
        print("   ", list(k[-6:] for k in self.pixmapDict.keys()))
        if "/" in path:
            compID, imgID = path.split("/", 1)
            self._delCompCache(compID)  # 先清缓存
            if imgID in self.pixmapDict:
                self.compDict[compID] = imgID  # 记录缓存
                return self.pixmapDict[imgID]
            print(f"[Error] imgID {imgID} not in pixmapDict!")
        else:  # 清空一个组件的缓存
            self._delCompCache(path)
        return self._getNoneImg()  # 返回占位符

后面的 _delCompCache 函数改为:

    def _delCompCache(self, compID):
        if compID in self.compDict:
            last = self.compDict[compID]
            if last in self.pixmapDict:
                print(f"___del: {compID} {last}")
                del self.pixmapDict[last]
            del self.compDict[compID]
Snipaste_2024-01-02_09-20-45

而且有个很迷的问题,在我用HDMI外接显示屏的时候会出现这问题,但是拔掉HDMI,只用笔记本电脑的屏幕,就没有异常颜色块。。。

hiroi-sora commented 10 months ago

Umi进行一次截图的流程是:

1. ScreenshotManager.qml 调用 screenshot 函数,开始截图流程
2. screenshot_controller.py 调用 getScreenshot 函数,返回所有N个屏幕的截图
3. ScreenshotManager.qml 生成N个 ScreenshotWindowComp 窗口,覆盖在N个屏幕上
4. 用户鼠标划选区域
5. ScreenshotManager.qml 调用 ssEnd 函数,销毁N个窗口,删除图片缓存
6. 裁切指定范围的图片,结束截图流程

而在你的截图日志中,看到这一行出现了两次: request:Image__QMLTYPE_160(0x253586402b0)/……cf517c 这是一个异常情况。在第3步,图片组件 0x253586402b0 重复请求 了编号为……cf517c的缓存。第二次请求时,按照我的缓存自动清理机制,……cf517c已经被清理了,已经不存在了。所以只能返回红-蓝-红的缺省图片。(这个颜色是人为设定的缺省图片)。

按理说,图片组件是不会重复请求id相同的缓存的。不知道为什么在你的设备上出现了重复的情况。

不过,既然知道问题发生的直接原因,咱们就能想办法绕开它。请打开UmiOCR-data\py_src\image_controller\image_provider.py,大约34行,原本是这样的:

            compID, imgID = path.split("/", 1)
            self._delCompCache(compID)  # 先清缓存

现在改动self._delCompCache这句,加个限制条件,防止重复请求时删除缓存:

            compID, imgID = path.split("/", 1)
            if compID in self.compDict and self.compDict[compID] != imgID: # 判断不是重复请求
                self._delCompCache(compID)  # 再清缓存

改动后,你的电脑上应该能正常使用双屏截图了。

lzh1043060917 commented 10 months ago

Umi进行一次截图的流程是:

1. ScreenshotManager.qml 调用 screenshot 函数,开始截图流程
2. screenshot_controller.py 调用 getScreenshot 函数,返回所有N个屏幕的截图
3. ScreenshotManager.qml 生成N个 ScreenshotWindowComp 窗口,覆盖在N个屏幕上
4. 用户鼠标划选区域
5. ScreenshotManager.qml 调用 ssEnd 函数,销毁N个窗口,删除图片缓存
6. 裁切指定范围的图片,结束截图流程

而在你的截图日志中,看到这一行出现了两次: request:Image__QMLTYPE_160(0x253586402b0)/……cf517c 这是一个异常情况。在第3步,图片组件 0x253586402b0 重复请求 了编号为……cf517c的缓存。第二次请求时,按照我的缓存自动清理机制,……cf517c已经被清理了,已经不存在了。所以只能返回红-蓝-红的缺省图片。(这个颜色是人为设定的缺省图片)。

按理说,图片组件是不会重复请求id相同的缓存的。不知道为什么在你的设备上出现了重复的情况。

不过,既然知道问题发生的直接原因,咱们就能想办法绕开它。请打开UmiOCR-data\py_src\image_controller\image_provider.py,大约34行,原本是这样的:

            compID, imgID = path.split("/", 1)
            self._delCompCache(compID)  # 先清缓存

现在改动self._delCompCache这句,加个限制条件,防止重复请求时删除缓存:

            compID, imgID = path.split("/", 1)
            if compID in self.compDict and self.compDict[compID] != imgID: # 判断不是重复请求
                self._delCompCache(compID)  # 再清缓存

改动后,你的电脑上应该能正常使用双屏截图了。

差不多能用了,不过还是有点小bug,我刚刚在截图的时候,外接屏幕的内容覆盖了一部分当前屏幕的内容。只能在笔记本电脑自身的屏幕这边截图,不能在外接屏幕那边截图。搞不好是设备哪里有问题,估计很难排查。如果太难搞就算了。谢谢了。现在也不是不能用

Snipaste_2024-01-03_09-22-24