Repository navigation
Fix crash when deleting an image & jump to the next unlabeled one afterwards - #269
Open
Jammy123-prog wants to merge 2 commits into
Open
Jammy123-prog wants to merge 2 commits into
Jammy123-prog wants to merge 2 commits into
Conversation
Root cause: after the image is moved to the trash, deleteImg() calls importDirImages(isDelete=True), which first goes through mayContinue() -> saveFile(). At that point self.filePath still points to the deleted file, so _saveFile() fails with FileNotFoundError in cv2.imdecode and the application exits. This is easily triggered when the 'auto save unsaved changes' option is enabled. Fix: - saveFile(): skip saving and log a warning when the file no longer exists on disk. - deleteImg(): clear self.filePath and the dirty flag before reloading the directory so the stale reference cannot be touched anymore. Verified with a regression test reproducing the exact call chain (open dir -> mark dirty -> remove file -> saveFile(Auto)): crashes before the fix, passes cleanly after it.
Jammy123-prog
force-pushed
the
fix/delete-image-crash
branch
from
October 21, 2026 01:49
5beb9ee to
459e2c1
Compare
The crash fix clears self.filePath before importDirImages() rescans the folder, so openNextImg() falls back to selecting the very first image of the directory and the annotator loses their position in the list. Restore position-aware navigation and improve it: - deleteImg() records the index of the deleted image and passes it to importDirImages(..., isDelete=True, deleteIdx=...). - importDirImages() maps that index onto the rescanned list (the same index now points at the image that followed the deleted one) and tells openNextImg() to prefer an unlabeled target. - openNextImg(preferUnlabeled=True) skips forward from that position to the first image whose validFilestate() is False, i.e. the next image that still needs annotation. If all following images are already labeled it keeps the previous fallback (the image right after the deleted one, or the previous one when the last image was deleted). Verified with three scenarios: delete first of three with the second already labeled -> third is selected; delete first with the rest labeled -> second is selected; delete the last one -> previous is selected.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
PPOCRLabel crashes (exits without any dialog) when the user deletes an image while the "auto save unsaved changes" option is enabled (and also whenever there are unsaved changes on the deleted image).
Root cause
After the image is moved to the trash,
deleteImg()callsimportDirImages(..., isDelete=True), which first goes throughmayContinue()→saveFile(). At that momentself.filePathstill points to the file that has just been deleted, so_saveFile()crashes incv2.imdecode(np.fromfile(...)):Fix (13 lines, no behaviour change for normal saves)
saveFile(): skip saving (with a warning log) when the target file no longer exists on disk.deleteImg(): drop the stale reference (self.filePath = None,self.dirty = False) before reloading the directory, so nothing can re-read the deleted image.Verification
Reproduced the exact call chain in a regression test (open dir → mark dirty → remove file →
saveFile(mode="Auto")+ metadata saves):Label.txt/Cache.cach/fileState.txtare written correctlyManual GUI test on Windows 11 + PaddleOCR 3.7.0 + custom det/rec models: deleting images no longer crashes the app.
中文说明
问题:开启"自动保存未提交变更"后删除图片,程序直接闪退。
根因:图片移入回收站后,
deleteImg()→importDirImages(isDelete=True)→mayContinue()→saveFile()链路仍持有self.filePath(已删除路径),cv2.imdecode(np.fromfile(...))抛FileNotFoundError崩溃。修复:①
saveFile()增加文件存在性检查,文件不存在则跳过并记录 warning;②deleteImg()删除成功后立即清空filePath引用并复位dirty标志。共 13 行新增,不影响正常保存逻辑。已通过回归测试与 GUI 手工验证。Follow-up (2nd commit): jump to the next unlabeled image after deletion
The crash fix clears
self.filePathbeforeimportDirImages()rescans the folder, soopenNextImg()falls back to selecting the very first image of the directory — the annotator loses their position in the list after every deletion.Restore position-aware navigation and improve it:
deleteImg()records the index of the deleted image and passes it toimportDirImages(..., isDelete=True, deleteIdx=...).importDirImages()maps that index onto the rescanned list (the same index then points at the image that followed the deleted one) and asksopenNextImg()to prefer an unlabeled target.openNextImg(preferUnlabeled=True)skips forward to the first image whosevalidFilestate()isFalse— the next image that still needs annotation. If all following images are already labeled, it keeps the previous fallback (the image right after the deleted one, or the previous one when the last image was deleted).Verified with three scenarios (3-image folder):
补充说明(第二个提交):删除后跳转到下一张未标注图片
崩溃修复会先清空
self.filePath,导致重扫列表后openNextImg()总是选中目录里的第一张图片,标注位置丢失。本提交记录被删图片的索引并传递给重扫逻辑,删除后自动跳到下一张未标注(validFilestate()为 False)的图片;若后续均已标注则回退为紧邻的下一张;删除最后一张时回退为前一张。三个场景回归测试全部通过。