Skip to content

Optimize commission suffix recognition and fix campaign issues#399

Merged
wess09 merged 11 commits into
masterfrom
dev
Jun 25, 2026
Merged

Optimize commission suffix recognition and fix campaign issues#399
wess09 merged 11 commits into
masterfrom
dev

Conversation

@wess09

@wess09 wess09 commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Summary by Sourcery

将佣金后缀文本的 OCR 替换为基于图像裁剪和相似度匹配的方案,并调整相关日志记录、实时预览稳定性、活动配置以及多处 UI 素材模板。

New Features:

  • 将佣金后缀以裁剪后的图像加 MD5 哈希的形式进行采集和存储,用于后续比对和日志记录。
  • 在佣金对比中引入基于图像的后缀相似度匹配。

Bug Fixes:

  • 在 ws-scrcpy 预卷和实时预览循环中正确处理 None 和空字节帧,避免过早结束或误解析。
  • 通过设备端 PID 和进程 cmdline 检测正在运行的 ws-scrcpy 服务,而不是仅依赖本地端口可用性。
  • 更正 20260625 CN 活动的活动入口元数据,包括英文和日文标题。
  • 通过禁用错误的模式切换标志,修复 event_20260625_cn 的 SP 地图配置。
  • 调整战斗奖励、空袭确认/取消按钮以及岛屿港口营业完成的 UI 素材模板和检测区域,使其在不同地区与游戏内视觉效果保持一致。

Enhancements:

  • 优化后缀裁剪逻辑,动态确定最右侧文本边界,并裁剪更聚焦的图像区域,以提高识别稳定性。
  • 更新 ws-scrcpy 客户端连接参数,调优 ping 和关闭超时行为,使实时预览会话更加稳健。

Documentation:

  • 更新活动 README,使各语言中对 “Miracle by Midnight” 活动的命名更加准确。
Original summary in English

Summary by Sourcery

Replace commission suffix text OCR with image-based cropping and similarity matching, and adjust related logging, live preview stability, campaign configuration, and various UI asset templates.

New Features:

  • Capture and store commission suffix as a cropped image plus MD5 hash for later comparison and logging.
  • Introduce image-based suffix similarity matching when comparing commissions.

Bug Fixes:

  • Handle None and empty-byte frames correctly in ws-scrcpy preroll and live preview loops to avoid premature termination or misparsing.
  • Detect running ws-scrcpy server via device-side PID and process cmdline instead of relying solely on local port availability.
  • Correct campaign entry metadata for the 20260625 CN event, including English and Japanese titles.
  • Fix SP map configuration for event_20260625_cn by disabling an incorrect mode-switch flag.
  • Adjust UI asset templates and detection regions for combat rewards, air strike confirm/cancel buttons, and island port business completion to match in-game visuals across regions.

Enhancements:

  • Refine suffix cropping logic to dynamically determine the rightmost text boundary and crop a focused image region for more reliable recognition.
  • Update ws-scrcpy client connect parameters to tune ping and close timeout behavior for more robust live preview sessions.

Documentation:

  • Update campaign README with accurate naming for the Miracle by Midnight event across languages.

LmeSzinc and others added 11 commits June 24, 2026 00:55
* Opt: using pHash and template matching for commission suffix recognition

* Refactor: improve suffix image processing and hash calculation
* Opt: using template matching for commission suffix recognition (#5731)

* Opt: using pHash and template matching for commission suffix recognition

* Refactor: improve suffix image processing and hash calculation

* Revert "Upd: [JP] asset GET_ITEMS_X (#5718)" (#5751)

This reverts commit c852cff.

* Chore: move hashlib to local import

* Upd: [TW] Event entrance of Revelations of Dust Rerun (event_20230223_cn)

---------

Co-authored-by: guoh064 <50830808+guoh064@users.noreply.github.com>
- 更新 ROUTE_PORT_BUSINESS_COMPLETE 资源图,匹配当前港口商区路线完成入口

- 同步按钮识别区域与平均颜色,避免沿用旧位置导致误判
最新活动"美梦巡演:奇妙夜"sp关卡配置 `AzurPilot/campaign/event_20260625_cn/sp.py`
中`MAP_HAS_MODE_SWITCH`被错误设置为`True`,导致进入关卡时AzurPilot会寻找不存在的切换困难模式按钮,从而卡死在进入关卡界面。对照此前活动的
`sp.py` 文件将其设置为`True`

## Summary by Sourcery

Bug Fixes:
- 修正 20260625 CN SP 活动配置中的 `MAP_HAS_MODE_SWITCH` 标志,以防止客户端在进入 SP
关卡时发生卡死。

<details>
<summary>Original summary in English</summary>

## Summary by Sourcery

Bug Fixes:
- Correct the MAP_HAS_MODE_SWITCH flag in the 20260625 CN SP campaign
config so the client no longer hangs when entering the SP stage.

</details>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@wess09 wess09 merged commit 99fb660 into master Jun 25, 2026
8 checks passed
@sourcery-ai

sourcery-ai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Reviewer's Guide

本次 PR 将佣金后缀识别从「基于 OCR 的 commission suffix」替换为「基于图像裁剪的后缀区域 + 模板匹配」,同时强化 ws-scrcpy 服务器与 websocket 的处理逻辑,并根据近期活动与 UI 变动调整部分资源模板和战役配置。

ws-scrcpy 服务器启动与连接的时序图

sequenceDiagram
participant Client
participant WsScrcpySession as session
participant AdbConnection as connection
participant Device

Client->>session: start_server()
loop start_server
    session->>connection: adb_push(WS_SCRCPY_FILEPATH_LOCAL, WS_SCRCPY_FILEPATH_REMOTE)
    session->>connection: adb_forward("tcp:"+WS_SCRCPY_PORT)
    connection-->>session: local_port
    session->>session: _server_running()
    session->>connection: adb_shell("test -f " + WS_SCRCPY_PID_FILE_REMOTE + " && cat " + WS_SCRCPY_PID_FILE_REMOTE)
    connection-->>session: pid
    session->>connection: adb_shell("cat /proc/"+pid+"/cmdline")
    connection-->>session: cmdline
    alt server_running
        session-->>Client: reuse existing server
    else server_not_running
        session->>connection: adb_shell(_server_command())
        connection-->>session: output
    end
end

Client->>session: connect()
session->>Device: connect(url, max_size=None, ping_interval=None, close_timeout=1)
Device-->>session: websocket
session-->>Client: remote_ws established
Loading

文件级改动

Change Details Files
将基于 SuffixOcr 的后缀识别替换为基于裁剪后缀图像的提取、哈希以及相似度匹配,用于 Commission 解析与相等性比较。
  • 新增 crop_suffix_image 辅助函数,通过基于亮度的右边缘检测与动态回溯,从佣金名称截图中裁剪出后缀区域。
  • 新增 image_hash 工具函数,用于计算后缀图像的 MD5 哈希,用于日志记录与字符串表示。
  • Commission dataclass 字段从文本后缀改为 suffix_imagesuffix_hash,并更新 CN/JN/TW 解析路径,改为使用 crop_suffix_imageimage_hash,而非 OCR 或名称后缀抽取。
  • 修改 Commission.__str__,仅在存在后缀哈希时才包含 suffix_hash
  • 更新 Commission.__eq__,在日常与特定油种类判断中使用新的 suffix_match 方法,而非直接比较后缀字符串。
  • 实现 Commission.suffix_match,利用 cv2.matchTemplate 在可选相似度阈值下比较两张后缀图像,并处理后缀图像缺失的情况。
module/commission/project.py
改进 ws-scrcpy 服务器生命周期检测和 websocket 处理,以用于实时预览。
  • 调整初始 ws-scrcpy preroll 收集和实时预览循环,将 None 视为流结束,但忽略空字节帧。
  • 新增 WS_SCRCPY_PID_FILE_REMOTE 常量,用于在设备上跟踪远程 PID。
  • 将本地端口探测替换为 _server_running:从设备读取 PID,验证该进程的 cmdline 是否包含预期包名与版本,并据此决定是否复用现有服务器。
  • 更新 start_server,依赖 _server_running 在启动新服务器前检测是否已有可复用服务器。
  • 调整 websocket 连接参数,禁用自动 ping,并为实时预览会话设置较短的 close_timeout
module/webui/api.py
为 JP 和 TW UI 以及新布局重新调校战斗与处理器资源按钮定义。
  • 调整 JP 端的 GET_ITEMS_1/2/3 区域与颜色,以匹配更新后的战斗掉落弹窗位置。
  • 更新 TW 端的 AIR_STRIKE_CANCEL/CONFIRM 区域、颜色以及资源文件路径,改为 TW 专用图像。
module/combat/assets.py
module/handler/assets.py
修复港口商业路线的海岛日常交互路线完成检测布局。
  • 调整 ROUTE_PORT_BUSINESS_COMPLETE 的区域、颜色和按钮坐标,以匹配更新后的 UI 布局,同时保持文件路径不变。
module/island_daily_interact/assets.py
更新 20260625 CN 活动的战役配置,包括名称及地图模式切换行为。
  • 在战役 Readme 中补充 20260625「Miracle by Midnight」活动的 EN 和 JP 本地化名称。
  • event_20260625_cn 特殊地图配置中禁用 MAP_HAS_MODE_SWITCH
campaign/Readme.md
campaign/event_20260625_cn/sp.py

Tips and commands

Interacting with Sourcery

  • 触发新的代码审查: 在 Pull Request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论即可继续讨论。
  • 从审查评论生成 GitHub issue: 回复某条审查评论,要求 Sourcery 以该评论创建 issue。也可以直接在审查评论下回复 @sourcery-ai issue 来创建对应的 issue。
  • 生成 Pull Request 标题: 在 Pull Request 标题中任意位置写上 @sourcery-ai,即可随时生成标题。也可以在 Pull Request 中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 Pull Request 摘要: 在 Pull Request 描述正文中任意位置写上 @sourcery-ai summary,即可在指定位置生成 PR 摘要。也可以在 Pull Request 中评论 @sourcery-ai summary 来随时(重新)生成摘要。
  • 生成审查者指南: 在 Pull Request 中评论 @sourcery-ai guide,即可随时(重新)生成审查者指南。
  • 解决所有 Sourcery 评论: 在 Pull Request 中评论 @sourcery-ai resolve,即可标记所有 Sourcery 评论为已解决。当你已经处理完所有评论且不再需要看到它们时很有用。
  • 关闭所有 Sourcery 审查: 在 Pull Request 中评论 @sourcery-ai dismiss,即可关闭所有现有的 Sourcery 审查。尤其适用于你希望从一次全新的审查开始——别忘了再评论 @sourcery-ai review 来触发新的审查!

Customizing Your Experience

访问你的 dashboard 以:

  • 启用或禁用审查特性,例如 Sourcery 自动生成的 Pull Request 摘要、审查者指南等。
  • 更改审查语言。
  • 添加、删除或编辑自定义审查指令。
  • 调整其他审查设置。

Getting Help

Original review guide in English

Reviewer's Guide

This PR replaces commission suffix OCR with image-based suffix cropping and template matching, hardens ws-scrcpy server and websocket handling, and adjusts several asset templates and campaign configuration for recent events and UI changes.

Sequence diagram for ws-scrcpy server startup and connection

sequenceDiagram
participant Client
participant WsScrcpySession as session
participant AdbConnection as connection
participant Device

Client->>session: start_server()
loop start_server
    session->>connection: adb_push(WS_SCRCPY_FILEPATH_LOCAL, WS_SCRCPY_FILEPATH_REMOTE)
    session->>connection: adb_forward("tcp:"+WS_SCRCPY_PORT)
    connection-->>session: local_port
    session->>session: _server_running()
    session->>connection: adb_shell("test -f " + WS_SCRCPY_PID_FILE_REMOTE + " && cat " + WS_SCRCPY_PID_FILE_REMOTE)
    connection-->>session: pid
    session->>connection: adb_shell("cat /proc/"+pid+"/cmdline")
    connection-->>session: cmdline
    alt server_running
        session-->>Client: reuse existing server
    else server_not_running
        session->>connection: adb_shell(_server_command())
        connection-->>session: output
    end
end

Client->>session: connect()
session->>Device: connect(url, max_size=None, ping_interval=None, close_timeout=1)
Device-->>session: websocket
session-->>Client: remote_ws established
Loading

File-Level Changes

Change Details Files
Replace SuffixOcr-based suffix recognition with cropped image suffix extraction, hashing, and similarity-based matching in Commission parsing and equality.
  • Introduce crop_suffix_image helper to isolate suffix region from commission name screenshot using intensity-based right-edge detection and dynamic look-back.
  • Add image_hash utility to compute MD5 hashes of suffix images for logging and string representation.
  • Change Commission dataclass fields from textual suffix to suffix_image and suffix_hash, and update CN/JN/TW parsing paths to use crop_suffix_image and image_hash instead of OCR or name-suffix extraction.
  • Modify Commission.str to include suffix_hash only when present.
  • Update Commission.eq to use new suffix_match method for daily and specific oil genres instead of direct suffix string comparison.
  • Implement Commission.suffix_match to compare two suffix images via cv2.matchTemplate with optional similarity threshold, including handling of missing images.
module/commission/project.py
Improve ws-scrcpy server lifetime detection and websocket handling for live preview.
  • Change initial ws-scrcpy preroll collection and live preview loops to treat None as end-of-stream but ignore empty byte frames.
  • Introduce WS_SCRCPY_PID_FILE_REMOTE constant for remote PID tracking.
  • Replace local port probing with _server_running that reads PID from device, validates process cmdline contains expected package and version, and uses that to decide server reuse.
  • Update start_server to rely on _server_running for detecting existing server before starting a new one.
  • Adjust websocket connect parameters to disable automatic pings and set a short close_timeout for live preview sessions.
module/webui/api.py
Retune combat and handler asset button definitions for JP and TW UIs and new layouts.
  • Adjust GET_ITEMS_1/2/3 JP areas and colors to match updated combat drop dialog positions.
  • Update AIR_STRIKE_CANCEL/CONFIRM TW areas, colors, and asset file paths to TW-specific images.
module/combat/assets.py
module/handler/assets.py
Fix island daily interaction route completion detection layout for port business route.
  • Change ROUTE_PORT_BUSINESS_COMPLETE area, color, and button coordinates to match updated UI layout while keeping file paths consistent.
module/island_daily_interact/assets.py
Update campaign configuration for the 20260625 CN event including names and map mode switch behavior.
  • Fill in EN and JP localized names for the 20260625 "Miracle by Midnight" event in campaign Readme.
  • Disable MAP_HAS_MODE_SWITCH for event_20260625_cn special map configuration.
campaign/Readme.md
campaign/event_20260625_cn/sp.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并给出了一些总体性的反馈:

  • crop_suffix_imagesuffix_match 中,建议把当前散落在各处的“魔法数字”(阈值、look_back 距离、3 像素边距、相似度默认值)集中抽取成具名常量或配置,这样可以让后缀匹配行为在不同服务器之间更易于调优和理解。
  • 新增的 image_hash 辅助函数在每次调用时都会在函数内部导入 hashlib;将该导入移动到模块级,可以避免重复导入,并在紧密的佣金解析循环中降低该辅助函数的调用开销。
面向 AI Agent 的提示词
Please address the comments from this code review:

## Overall Comments
- In `crop_suffix_image` and `suffix_match`, consider centralizing the magic numbers (thresholds, look_back distance, 3‑pixel margins, similarity default) into named constants or config to make the suffix matching behavior easier to tune and reason about across servers.
- The new `image_hash` helper imports `hashlib` inside the function on every call; moving this import to module scope would avoid repeated imports and make the helper cheaper when used in tight commission parsing loops.

## Individual Comments

### Comment 1
<location path="module/commission/project.py" line_range="35-38" />
<code_context>
+    Returns:
+        后缀裁剪图,黑字白底;未检测到文字时返回 None。
+    """
+    name_image = crop(image, area)
+    name_image = extract_letters(name_image, letter=(255, 255, 255), threshold=128).astype(np.uint8)
+
+    line = cv2.reduce(name_image[5:-5, :], 0, cv2.REDUCE_AVG).flatten()
+    columns = np.where(line < 250)[0]
+    if not len(columns):
</code_context>
<issue_to_address>
**issue (bug_risk):** Guard against very small or empty name_image before slicing and reducing.

If the cropped region is very small (e.g., height ≤ 10 or width == 0), `name_image[5:-5, :]` may be empty and `cv2.reduce` will error. Consider checking `name_image.shape` first and returning None or using a fallback when the region is too small for the 5‑pixel trim.
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得我们的代码评审有帮助,欢迎帮忙分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进之后的评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • In crop_suffix_image and suffix_match, consider centralizing the magic numbers (thresholds, look_back distance, 3‑pixel margins, similarity default) into named constants or config to make the suffix matching behavior easier to tune and reason about across servers.
  • The new image_hash helper imports hashlib inside the function on every call; moving this import to module scope would avoid repeated imports and make the helper cheaper when used in tight commission parsing loops.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `crop_suffix_image` and `suffix_match`, consider centralizing the magic numbers (thresholds, look_back distance, 3‑pixel margins, similarity default) into named constants or config to make the suffix matching behavior easier to tune and reason about across servers.
- The new `image_hash` helper imports `hashlib` inside the function on every call; moving this import to module scope would avoid repeated imports and make the helper cheaper when used in tight commission parsing loops.

## Individual Comments

### Comment 1
<location path="module/commission/project.py" line_range="35-38" />
<code_context>
+    Returns:
+        后缀裁剪图,黑字白底;未检测到文字时返回 None。
+    """
+    name_image = crop(image, area)
+    name_image = extract_letters(name_image, letter=(255, 255, 255), threshold=128).astype(np.uint8)
+
+    line = cv2.reduce(name_image[5:-5, :], 0, cv2.REDUCE_AVG).flatten()
+    columns = np.where(line < 250)[0]
+    if not len(columns):
</code_context>
<issue_to_address>
**issue (bug_risk):** Guard against very small or empty name_image before slicing and reducing.

If the cropped region is very small (e.g., height ≤ 10 or width == 0), `name_image[5:-5, :]` may be empty and `cv2.reduce` will error. Consider checking `name_image.shape` first and returning None or using a fallback when the region is too small for the 5‑pixel trim.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +35 to +38
name_image = crop(image, area)
name_image = extract_letters(name_image, letter=(255, 255, 255), threshold=128).astype(np.uint8)

line = cv2.reduce(name_image[5:-5, :], 0, cv2.REDUCE_AVG).flatten()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): 在进行切片和归约之前,需要防范 name_image 过小或为空的情况。

如果裁剪区域非常小(例如高度 ≤ 10 或宽度 == 0),name_image[5:-5, :] 可能为空,而此时调用 cv2.reduce 会报错。建议先检查 name_image.shape,在区域太小不适合做 5 像素裁剪时直接返回 None,或采用其他回退处理方式。

Original comment in English

issue (bug_risk): Guard against very small or empty name_image before slicing and reducing.

If the cropped region is very small (e.g., height ≤ 10 or width == 0), name_image[5:-5, :] may be empty and cv2.reduce will error. Consider checking name_image.shape first and returning None or using a fallback when the region is too small for the 5‑pixel trim.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates event translations, adjusts UI button assets across different servers, and refactors the commission suffix recognition from OCR to image-based template matching. It also improves ws-scrcpy server detection in the WebUI by reading PID files and verifying process command lines. Feedback on the changes highlights potential crashes in the new image processing logic: slicing and cropping could fail on small images in crop_suffix_image and suffix_match, and an IndexError could occur when parsing an empty PID file.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +38 to +53
line = cv2.reduce(name_image[5:-5, :], 0, cv2.REDUCE_AVG).flatten()
columns = np.where(line < 250)[0]
if not len(columns):
return None

# 从最右侧文字向左回看,尽量完整包含罗马数字后缀。
threshold = 250
look_back = 10
for i in range(columns[-1], 0, -1):
if line[i] > threshold:
if columns[-1] - i > look_back:
look_back = columns[-1] - i
break

left = columns[-1] - look_back
right = columns[-1] + 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If name_image has a height of 10 or less, slicing name_image[5:-5, :] will result in an empty array, causing cv2.reduce to crash. Additionally, if columns[-1] is less than look_back (10), left will become negative, which can lead to out-of-bounds cropping or unexpected behavior.

We should add a guard for the image height and ensure that left and right are clamped within the valid boundaries of name_image.

Suggested change
line = cv2.reduce(name_image[5:-5, :], 0, cv2.REDUCE_AVG).flatten()
columns = np.where(line < 250)[0]
if not len(columns):
return None
# 从最右侧文字向左回看,尽量完整包含罗马数字后缀。
threshold = 250
look_back = 10
for i in range(columns[-1], 0, -1):
if line[i] > threshold:
if columns[-1] - i > look_back:
look_back = columns[-1] - i
break
left = columns[-1] - look_back
right = columns[-1] + 1
h, w = name_image.shape[:2]
if h > 10:
line = cv2.reduce(name_image[5:-5, :], 0, cv2.REDUCE_AVG).flatten()
else:
line = cv2.reduce(name_image, 0, cv2.REDUCE_AVG).flatten()
columns = np.where(line < 250)[0]
if not len(columns):
return None
# 从最右侧文字向左回看,尽量完整包含罗马数字后缀。
threshold = 250
look_back = 10
for i in range(columns[-1], 0, -1):
if line[i] > threshold:
if columns[-1] - i > look_back:
look_back = columns[-1] - i
break
left = max(0, columns[-1] - look_back)
right = min(w, columns[-1] + 1)

Comment on lines +437 to +444
def match(image, template):
template = crop(template, (3, 3, template.shape[1] - 3, template.shape[0] - 3), copy=False)
if image.shape[0] < template.shape[0] or image.shape[1] < template.shape[1]:
return 0.0

res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
_, sim, _, _ = cv2.minMaxLoc(res)
return sim

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If the template width or height is 6 or less (which can happen for narrow characters like "I" or due to tight cropping), template.shape[1] - 3 will be 3 or less, resulting in an invalid crop area where x1 >= x2 or y1 >= y2. This causes crop to return an empty image, which will crash cv2.matchTemplate with an assertion error.

We should check if the template dimensions are too small and return 0.0 early to prevent crashes.

        def match(image, template):
            h, w = template.shape[:2]
            if h <= 6 or w <= 6:
                return 0.0
            template = crop(template, (3, 3, w - 3, h - 3), copy=False)
            if image.shape[0] < template.shape[0] or image.shape[1] < template.shape[1]:
                return 0.0

            res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
            _, sim, _, _ = cv2.minMaxLoc(res)
            return sim

Comment thread module/webui/api.py
Comment on lines +530 to +534
try:
pid = self.connection.adb_shell(f"test -f {WS_SCRCPY_PID_FILE_REMOTE} && cat {WS_SCRCPY_PID_FILE_REMOTE}", timeout=2)
pid = str(pid or "").strip().split()[0]
except Exception:
pid = ""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If the adb_shell command returns an empty string or only whitespace (e.g., if the PID file is empty or doesn't exist), split() will return an empty list, and accessing [0] will raise an IndexError. Although this is caught by the except Exception: block, relying on exceptions for expected control flow is an anti-pattern and can make debugging harder.

We should use a safe list access to retrieve the PID.

Suggested change
try:
pid = self.connection.adb_shell(f"test -f {WS_SCRCPY_PID_FILE_REMOTE} && cat {WS_SCRCPY_PID_FILE_REMOTE}", timeout=2)
pid = str(pid or "").strip().split()[0]
except Exception:
pid = ""
try:
pid_out = self.connection.adb_shell(f"test -f {WS_SCRCPY_PID_FILE_REMOTE} && cat {WS_SCRCPY_PID_FILE_REMOTE}", timeout=2)
pids = str(pid_out or "").strip().split()
pid = pids[0] if pids else ""
except Exception:
pid = ""

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants