最新推送
This commit is contained in:
BIN
muban/1.png
Normal file
BIN
muban/1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 MiB |
69
muban/caijian.py
Normal file
69
muban/caijian.py
Normal file
@ -0,0 +1,69 @@
|
||||
import os
|
||||
import cv2
|
||||
|
||||
# =========================================================
|
||||
# 配置
|
||||
# =========================================================
|
||||
|
||||
SRC_DIR = "muban_image" # 原始模板目录
|
||||
DST_DIR = "muban_image2" # 裁剪后保存目录
|
||||
|
||||
# 三个 ROI(x, y, w, h)
|
||||
ROI_1 = (782, 614, 164, 128)
|
||||
ROI_2 = (837, 791, 100, 99)
|
||||
ROI_3 = (873, 736, 141, 110)
|
||||
|
||||
|
||||
# =========================================================
|
||||
# 裁剪函数
|
||||
# =========================================================
|
||||
def crop_and_save(img_path, save_dir):
|
||||
img = cv2.imread(img_path)
|
||||
if img is None:
|
||||
print(f"[WARN] 读取失败: {img_path}")
|
||||
return
|
||||
|
||||
h_img, w_img = img.shape[:2]
|
||||
base = os.path.splitext(os.path.basename(img_path))[0]
|
||||
|
||||
roi_list = [ROI_1, ROI_2, ROI_3]
|
||||
|
||||
for idx, roi in enumerate(roi_list, start=1):
|
||||
x, y, w, h = roi
|
||||
|
||||
# 边界保护
|
||||
x1 = max(0, x)
|
||||
y1 = max(0, y)
|
||||
x2 = min(w_img, x + w)
|
||||
y2 = min(h_img, y + h)
|
||||
|
||||
if x2 <= x1 or y2 <= y1:
|
||||
print(f"[WARN] ROI_{idx} 超出图像范围: {img_path}")
|
||||
continue
|
||||
|
||||
roi_img = img[y1:y2, x1:x2]
|
||||
|
||||
save_name = f"{base}_roi{idx}.png"
|
||||
save_path = os.path.join(save_dir, save_name)
|
||||
cv2.imwrite(save_path, roi_img)
|
||||
|
||||
print(f"[OK] 保存: {save_path}")
|
||||
|
||||
|
||||
# =========================================================
|
||||
# main
|
||||
# =========================================================
|
||||
if __name__ == "__main__":
|
||||
if not os.path.isdir(SRC_DIR):
|
||||
raise RuntimeError(f"源目录不存在: {SRC_DIR}")
|
||||
|
||||
os.makedirs(DST_DIR, exist_ok=True)
|
||||
|
||||
for fname in os.listdir(SRC_DIR):
|
||||
if not fname.lower().endswith((".png", ".jpg", ".jpeg", ".bmp")):
|
||||
continue
|
||||
|
||||
img_path = os.path.join(SRC_DIR, fname)
|
||||
crop_and_save(img_path, DST_DIR)
|
||||
|
||||
print("\n[INFO] 所有图片裁剪完成")
|
||||
46
muban/choose-roi.py
Normal file
46
muban/choose-roi.py
Normal file
@ -0,0 +1,46 @@
|
||||
import cv2
|
||||
import os
|
||||
|
||||
def select_and_output_roi(img_path, save=True, save_dir="roi_output"):
|
||||
img = cv2.imread(img_path)
|
||||
if img is None:
|
||||
raise RuntimeError("无法读取图片")
|
||||
|
||||
# OpenCV 自带 ROI 选择工具
|
||||
roi = cv2.selectROI(
|
||||
windowName="Select ROI (ENTER or C to confirm, ESC to quit)",
|
||||
img=img,
|
||||
showCrosshair=True,
|
||||
fromCenter=False
|
||||
)
|
||||
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
x, y, w, h = roi
|
||||
print(f"[INFO] ROI = (x={x}, y={y}, w={w}, h={h})")
|
||||
|
||||
roi_img = img[y:y+h, x:x+w]
|
||||
|
||||
if save:
|
||||
os.makedirs(save_dir, exist_ok=True)
|
||||
save_path = os.path.join(save_dir, "roi.png")
|
||||
cv2.imwrite(save_path, roi_img)
|
||||
print(f"[INFO] ROI 图像已保存: {save_path}")
|
||||
|
||||
return roi, roi_img
|
||||
|
||||
|
||||
# ----------------------------
|
||||
# main 测试
|
||||
# ----------------------------
|
||||
if __name__ == "__main__":
|
||||
IMAGE_PATH = "test2.png" # 换成你的图片
|
||||
|
||||
roi, roi_img = select_and_output_roi(
|
||||
IMAGE_PATH,
|
||||
save=True
|
||||
)
|
||||
|
||||
cv2.imshow("ROI Result", roi_img)
|
||||
cv2.waitKey(0)
|
||||
cv2.destroyAllWindows()
|
||||
165
muban/main.py
Normal file
165
muban/main.py
Normal file
@ -0,0 +1,165 @@
|
||||
import os
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
# =========================================================
|
||||
# 全局配置
|
||||
# =========================================================
|
||||
|
||||
TEMPLATE_DIR = "./muban_image1"
|
||||
|
||||
# 三个 ROI(x, y, w, h)
|
||||
ROI_1 = (782, 614, 164, 128)
|
||||
ROI_2 = (837, 791, 100, 99)
|
||||
ROI_3 = (873, 736, 141, 110)
|
||||
|
||||
USE_GRAY = False
|
||||
TM_METHOD = cv2.TM_CCOEFF_NORMED
|
||||
SCORE_THRESH = 0.4
|
||||
|
||||
|
||||
# =========================================================
|
||||
# 模板加载
|
||||
# =========================================================
|
||||
def load_templates(template_dir):
|
||||
templates = []
|
||||
if not os.path.isdir(template_dir):
|
||||
raise RuntimeError(f"模板目录不存在: {template_dir}")
|
||||
|
||||
for fname in os.listdir(template_dir):
|
||||
if not fname.lower().endswith((".png", ".jpg", ".jpeg", ".bmp")):
|
||||
continue
|
||||
|
||||
path = os.path.join(template_dir, fname)
|
||||
img = cv2.imread(path)
|
||||
if img is None:
|
||||
continue
|
||||
|
||||
if USE_GRAY:
|
||||
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
h, w = img.shape[:2]
|
||||
name = os.path.splitext(fname)[0]
|
||||
|
||||
templates.append({
|
||||
"name": name,
|
||||
"img": img,
|
||||
"h": h,
|
||||
"w": w
|
||||
})
|
||||
|
||||
print(f"[INFO] 已加载 {len(templates)} 个模板")
|
||||
return templates
|
||||
|
||||
|
||||
TEMPLATES = load_templates(TEMPLATE_DIR)
|
||||
|
||||
|
||||
# =========================================================
|
||||
# ROI 裁剪
|
||||
# =========================================================
|
||||
def crop_roi(img, roi):
|
||||
x, y, w, h = roi
|
||||
return img[y:y + h, x:x + w]
|
||||
|
||||
|
||||
# =========================================================
|
||||
# 单 ROI 模板匹配
|
||||
# =========================================================
|
||||
def match_one_roi(img, roi):
|
||||
roi_img = crop_roi(img, roi)
|
||||
if roi_img.size == 0:
|
||||
return None, 0.0
|
||||
|
||||
if USE_GRAY:
|
||||
roi_img = cv2.cvtColor(roi_img, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
best_name = None
|
||||
best_score = -1.0
|
||||
|
||||
for t in TEMPLATES:
|
||||
if t["h"] > roi_img.shape[0] or t["w"] > roi_img.shape[1]:
|
||||
continue
|
||||
|
||||
res = cv2.matchTemplate(roi_img, t["img"], TM_METHOD)
|
||||
_, max_val, _, _ = cv2.minMaxLoc(res)
|
||||
|
||||
if max_val > best_score:
|
||||
best_score = max_val
|
||||
best_name = t["name"]
|
||||
|
||||
if best_score < SCORE_THRESH:
|
||||
return None, best_score
|
||||
|
||||
return best_name, best_score
|
||||
|
||||
|
||||
# =========================================================
|
||||
# 核心逻辑:ROI_1 → ROI_2 → ROI_3
|
||||
# =========================================================
|
||||
def match_image(img):
|
||||
roi_list = [
|
||||
("roi1", ROI_1),
|
||||
("roi2", ROI_2),
|
||||
("roi3", ROI_3),
|
||||
]
|
||||
|
||||
for roi_name, roi in roi_list:
|
||||
name, score = match_one_roi(img, roi)
|
||||
if name is not None:
|
||||
return {
|
||||
"result": name,
|
||||
"roi": roi_name,
|
||||
"score": score
|
||||
}
|
||||
|
||||
return {
|
||||
"result": None,
|
||||
"roi": None,
|
||||
"score": 0.0
|
||||
}
|
||||
|
||||
|
||||
# =========================================================
|
||||
# 可视化(只画命中的 ROI)
|
||||
# =========================================================
|
||||
def draw_result(img, result):
|
||||
vis = img.copy()
|
||||
|
||||
roi_map = {
|
||||
"roi1": ROI_1,
|
||||
"roi2": ROI_2,
|
||||
"roi3": ROI_3
|
||||
}
|
||||
|
||||
if result["roi"] not in roi_map:
|
||||
return vis
|
||||
|
||||
x, y, w, h = roi_map[result["roi"]]
|
||||
text = f'{result["result"]} {result["score"]:.2f}'
|
||||
|
||||
cv2.rectangle(vis, (x, y), (x + w, y + h), (0, 255, 0), 2)
|
||||
cv2.putText(
|
||||
vis, text, (x, y - 10),
|
||||
cv2.FONT_HERSHEY_SIMPLEX,
|
||||
0.7, (0, 255, 0), 2
|
||||
)
|
||||
|
||||
return vis
|
||||
|
||||
|
||||
# =========================================================
|
||||
# main 测试
|
||||
# =========================================================
|
||||
if __name__ == "__main__":
|
||||
img = cv2.imread("1.png")
|
||||
if img is None:
|
||||
raise RuntimeError("测试图片未找到")
|
||||
|
||||
result = match_image(img)
|
||||
print("[RESULT]", result)
|
||||
|
||||
vis = draw_result(img, result)
|
||||
cv2.imshow("template match", vis)
|
||||
cv2.waitKey(0)
|
||||
cv2.destroyAllWindows()
|
||||
Reference in New Issue
Block a user