重啟撲克機器人之路 -3 :從OCR到程式碼重構的一天

更新於 發佈於 閱讀時間約 8 分鐘

今天繼續在撲克桌況辨識上奮鬥,決定使用OCR來處理文字辨識的部分。相較於OpenHoldem時期需要一個個擷取數字Template進行比對的方式,OCR確實帶來了效率的提升,短短半小時就完成了基礎的辨識功能。

raw-image

然而道路總是充滿意想不到的挑戰。在測試不同撲克軟體時,發現介面上的英文字會干擾數字辨識的準確度。原本想說把它們納入辨識區域應該不會有什麼問題,反正在辨識後再取用數字部分即可,結果卻造成了意外的困擾。尋求AI建議後,得到的解決方案反而越來越複雜,就為了處理這麼一個看似簡單的問題。最後我選擇了更務實的做法,直接略過英文字只擷取數字的Region。這個決定讓我再次體會到,在實作中,找到一個「夠用」的解決方案,有時比追求完美的解法更重要。

raw-image

下午的時候,開始著手整理Project的程式碼結構。過去總是習慣把所有程式碼都寫在一個Jupyter Notebook裡,結構鬆散且難以維護。今年我決定提早開始規劃程式碼架構,希望能避免之前的慘痛教訓。在AI的指導下,開始把不同功能的程式碼分門別類地放入適當的資料夾中。

poker_detector/
├── src/
│ ├── __init__.py
│ ├── detector/
│ │ ├── __init__.py
│ │ ├── card_detector.py
│ │ ├── text_detector.py
│ │ └── template_matcher.py
│ ├── models/
│ │ ├── __init__.py
│ │ └── card.py
│ ├── utils/
│ │ ├── __init__.py
│ │ ├── image_preprocessing.py
│ │ └── device_connector.py
│ └── config/
│ ├── __init__.py
│ └── regions.py
├── main.py
└── requirements.txt

這個重構過程中遇到了Python模組匯入的老問題 - 從不同資料夾匯入類別或函式總是會出錯。有趣的是,這個問題我之前就遇過好幾次,每次解決後卻總是忘記解法,下次又得重新摸索。這次我決定把解決方案好好記錄下來:必須在每個資料夾建立__init__.py檔案,並在主程式中正確設定路徑。這個看似簡單的問題卻花了我半小時才解決,但至少這次的經驗提醒了我確實記錄的重要性。

import sys
import os

# Get the directory of the current file and append the project root to sys.path

sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
#text_detector.py

import cv2

import pytesseract

from src.utils.image_preprocessing import ImagePreprocessor

import numpy as np



class TextDetector:

@staticmethod

def extract_number(text: str) -> float:

numbers = ''.join(c for c in text if c.isdigit() or c == '.')

try:

return float(numbers)

except ValueError:

return 0.0



def detect_text(self, roi: np.ndarray, is_dark_background: bool = False) -> str:

if is_dark_background:

processed = ImagePreprocessor.preprocess_for_ocr_dark_background(roi)

else:

processed = ImagePreprocessor.preprocess_for_ocr(roi)

return pytesseract.image_to_string(processed, config='--psm 7 digits')



def detect_value(self, roi: np.ndarray, is_dark_background: bool = False) -> float:

text = self.detect_text(roi, is_dark_background)

return self.extract_number(text)
#image_preprocessing.py
import cv2

import numpy as np



class ImagePreprocessor:

@staticmethod

def preprocess_for_template(image: np.ndarray) -> np.ndarray:

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

binary = cv2.adaptiveThreshold(

gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,

cv2.THRESH_BINARY_INV, 11, 2

)

kernel = np.ones((3,3), np.uint8)

binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)

return binary



@staticmethod

def preprocess_for_ocr(roi: np.ndarray) -> np.ndarray:

gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)

_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

scaled = cv2.resize(binary, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)

denoised = cv2.fastNlMeansDenoising(scaled)

return denoised
留言
avatar-img
留言分享你的想法!
avatar-img
傑劉的沙龍
3會員
18內容數
傑劉的沙龍的其他內容
2025/03/16
記錄了對撲克數據庫程式碼的深入理解,以及如何通過精確的查詢獲得準確的分析結果。通過重新組織action type的分類,讓後續的數據分析變得更加高效。這個數據庫將是撲克機器人專案的重要組成部分,用於建立更精確的對手模型。
Thumbnail
2025/03/16
記錄了對撲克數據庫程式碼的深入理解,以及如何通過精確的查詢獲得準確的分析結果。通過重新組織action type的分類,讓後續的數據分析變得更加高效。這個數據庫將是撲克機器人專案的重要組成部分,用於建立更精確的對手模型。
Thumbnail
2025/03/14
記錄了在建構撲克數據庫過程中遇到的挑戰和收穫。探討了自建系統與現成工具的差異,以及如何確保數據準確性。同時反思了精確表達查詢需求的重要性,以及自建系統潛在的長期價值。
Thumbnail
2025/03/14
記錄了在建構撲克數據庫過程中遇到的挑戰和收穫。探討了自建系統與現成工具的差異,以及如何確保數據準確性。同時反思了精確表達查詢需求的重要性,以及自建系統潛在的長期價值。
Thumbnail
2025/03/13
記錄了在撲克機器人開發中從機器學習模型轉向建立自定義數據庫的過程,以及這個策略轉變背後的思考。通過分析真實玩家的行動分布,希望能訓練出更有效的撲克機器人。
Thumbnail
2025/03/13
記錄了在撲克機器人開發中從機器學習模型轉向建立自定義數據庫的過程,以及這個策略轉變背後的思考。通過分析真實玩家的行動分布,希望能訓練出更有效的撲克機器人。
Thumbnail
看更多
你可能也想看
Thumbnail
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
Thumbnail
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
Thumbnail
本篇介紹單人遊戲的核心架構與邏輯,涵蓋發牌、抽牌、出牌及遊戲結算等重要步驟。文章也詳細介紹了使用 socket.io 建立連線的過程,並說明如何利用 React Hooks 管理遊戲狀態,提及後端伺服器如何處理玩家加入房間的事件,並簡要介紹了房間資訊的管理,此文將分為多篇進一步介紹遊戲事件部分。
Thumbnail
本篇介紹單人遊戲的核心架構與邏輯,涵蓋發牌、抽牌、出牌及遊戲結算等重要步驟。文章也詳細介紹了使用 socket.io 建立連線的過程,並說明如何利用 React Hooks 管理遊戲狀態,提及後端伺服器如何處理玩家加入房間的事件,並簡要介紹了房間資訊的管理,此文將分為多篇進一步介紹遊戲事件部分。
Thumbnail
原版的官方規則導入記分機制,但因為計算過於繁複,所以一般遊玩時較少採用。本變體規則旨在還原原規則的策略性,並保留平常的遊玩樂趣。 1. 配件準備 4枚不同顏色的棋子(紅、藍、黃、綠),以及一張標記0~15的場地。 2. 記分方式 一開始所有棋子都在0的位置。每一局結束時,贏家以外的所有人拿出
Thumbnail
原版的官方規則導入記分機制,但因為計算過於繁複,所以一般遊玩時較少採用。本變體規則旨在還原原規則的策略性,並保留平常的遊玩樂趣。 1. 配件準備 4枚不同顏色的棋子(紅、藍、黃、綠),以及一張標記0~15的場地。 2. 記分方式 一開始所有棋子都在0的位置。每一局結束時,贏家以外的所有人拿出
Thumbnail
演算法就像盲盒,在你滑下一篇前,你永遠不會知道下一篇內容是甚麼。可好處是,這個盲盒是免費的、是廉價的,你可以用大量的時間去「刷」,你可以盡量刷,直到刷到自己喜歡的內容。有時候被低劣的內容吸引也沒關係,畢竟是免費的。可有時會刷到精緻、感興趣的內容,那就太幸運了,把他存起來,然後繼續刷。
Thumbnail
演算法就像盲盒,在你滑下一篇前,你永遠不會知道下一篇內容是甚麼。可好處是,這個盲盒是免費的、是廉價的,你可以用大量的時間去「刷」,你可以盡量刷,直到刷到自己喜歡的內容。有時候被低劣的內容吸引也沒關係,畢竟是免費的。可有時會刷到精緻、感興趣的內容,那就太幸運了,把他存起來,然後繼續刷。
Thumbnail
這幾個月很流行「AI塔羅占卜」,也就是望遠鏡塔羅AI。 很多人應該會很好奇,AI連這種事情都辦得到嗎? AI算得準嗎?AI塔羅跟人類占卜師有什麼差別? 電腦抽牌真的靈驗嗎?實體牌卡跟虛擬牌卡哪個準? 這篇會以我的實際經歷來說明這些疑問。
Thumbnail
這幾個月很流行「AI塔羅占卜」,也就是望遠鏡塔羅AI。 很多人應該會很好奇,AI連這種事情都辦得到嗎? AI算得準嗎?AI塔羅跟人類占卜師有什麼差別? 電腦抽牌真的靈驗嗎?實體牌卡跟虛擬牌卡哪個準? 這篇會以我的實際經歷來說明這些疑問。
Thumbnail
W5 3/18 《曼哈頓》 雖然遊戲規則感覺很簡單,但很重策略(對我來說),要一直思考如何分佈建築才能得高分,還有目標針對誰之類的,但我覺得設計的不錯,很有趣。 《機密代碼》 這款遊戲學校有,我也玩過,但是老師有改一個規則分攤隊長的壓力,我覺得很不錯,是很有效的規則,我們玩了兩局,都是紅隊贏
Thumbnail
W5 3/18 《曼哈頓》 雖然遊戲規則感覺很簡單,但很重策略(對我來說),要一直思考如何分佈建築才能得高分,還有目標針對誰之類的,但我覺得設計的不錯,很有趣。 《機密代碼》 這款遊戲學校有,我也玩過,但是老師有改一個規則分攤隊長的壓力,我覺得很不錯,是很有效的規則,我們玩了兩局,都是紅隊贏
Thumbnail
上周來開始把累積沒上的紫微課程拿出來上, 然後上到牌卡, 老師說的一句, 牌卡(其實盤也是)就像是你在學英文, 你光只是看書背單字, 在遇到外國人或是去國外時, 你還是一句話都不會說。 因為英文這個例子太有感, 所以我從星期二, 也是10月17日開始趁GMMTV發佈會那天開始每天試著抽一張牌,
Thumbnail
上周來開始把累積沒上的紫微課程拿出來上, 然後上到牌卡, 老師說的一句, 牌卡(其實盤也是)就像是你在學英文, 你光只是看書背單字, 在遇到外國人或是去國外時, 你還是一句話都不會說。 因為英文這個例子太有感, 所以我從星期二, 也是10月17日開始趁GMMTV發佈會那天開始每天試著抽一張牌,
Thumbnail
你一定有玩過猜拳遊戲,但你知道怎麼用Python寫一個猜拳遊戲嗎?今天我要分享一個簡單又好玩的程式碼,讓你可以和電腦對戰! 首先,我們要導入random模組,這個模組可以讓我們隨機生成一個數字,代表電腦出的拳。 然後,我們要用input函數讓使用者輸入自己出的拳,0代表剪刀,1代表石頭,2代表
Thumbnail
你一定有玩過猜拳遊戲,但你知道怎麼用Python寫一個猜拳遊戲嗎?今天我要分享一個簡單又好玩的程式碼,讓你可以和電腦對戰! 首先,我們要導入random模組,這個模組可以讓我們隨機生成一個數字,代表電腦出的拳。 然後,我們要用input函數讓使用者輸入自己出的拳,0代表剪刀,1代表石頭,2代表
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News