Skip to content

Commit 7a5dd65

Browse files
committed
feat(ff7-screenshots): add crop_game_window.py
1 parent 077e701 commit 7a5dd65

1 file changed

Lines changed: 126 additions & 0 deletions

File tree

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
4+
__author__ = "ipetrash"
5+
6+
7+
from pathlib import Path
8+
9+
# pip install opencv-python==4.13.0.92
10+
import cv2
11+
12+
13+
def crop_inner_game_screen(img: cv2.typing.MatLike) -> cv2.typing.MatLike | None:
14+
# 1. Переводим в оттенки серого
15+
gray: cv2.typing.MatLike = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
16+
17+
# 2. Бинаризация (Threshold)
18+
# Все, что чернее, чем 10 (по шкале от 0 до 255), станет абсолютно черным (0).
19+
# Все, что ярче (сама игра и интерфейс Windows), станет абсолютно белым (255).
20+
_, thresh = cv2.threshold(gray, 10, 255, cv2.THRESH_BINARY)
21+
22+
# 3. Находим ВСЕ контуры на получившейся черно-белой маске
23+
contours, _ = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
24+
25+
img_h, img_w = gray.shape
26+
total_area: int = img_h * img_w
27+
28+
valid_boxes: list[tuple[int, tuple[int, int, int, int]]] = []
29+
for contour in contours:
30+
# Находим ограничивающий прямоугольник для каждого контура
31+
x, y, w, h = cv2.boundingRect(contour)
32+
area: int = w * h
33+
34+
# Фильтруем контуры, чтобы отсечь мусор:
35+
# - Внутренний экран должен быть достаточно большим (например, больше 15% от всего скрина)
36+
# - Он не должен быть размером со весь экран (меньше 95%, чтобы отсечь сам скриншот)
37+
if (0.15 * total_area) < area < (0.95 * total_area):
38+
39+
# Дополнительная проверка: проверяем, что внутри этого прямоугольника
40+
# нет синей полоски заголовка Windows (проверяем пропорции или координаты)
41+
# Окно игры на этом скрине находится ближе к центру, y явно больше 0.
42+
if (y > 10 and (y + h)) < (img_h - 10):
43+
# TODO: remove
44+
# cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 1)
45+
# cv2.rectangle(thresh, (x, y), (x + w, y + h), (0, 0, 255), 1)
46+
47+
valid_boxes.append((area, (x, y, w, h)))
48+
49+
# TODO: remove
50+
# dsize = 1280, 720
51+
# cv2.imshow("thresh", cv2.resize(thresh, dsize))
52+
# cv2.imshow("contours", cv2.resize(img, dsize))
53+
# # cv2.imshow("cropped", cv2.resize(cropped, dsize))
54+
# cv2.resizeWindow("contours", *dsize)
55+
# cv2.waitKey(0)
56+
# cv2.destroyAllWindows()
57+
58+
if not valid_boxes: # TODO:
59+
print("Внутренний экран игры не найден. Возможно, кадр слишком темный.")
60+
return None
61+
62+
# Сортируем по площади. Нам нужен самый БОЛЬШОЙ прямоугольник,
63+
# который прошел фильтры и находится внутри черных полей.
64+
valid_boxes.sort(key=lambda item: item[0], reverse=True)
65+
_, (best_x, best_y, best_w, best_h) = valid_boxes[0]
66+
67+
# 4. Вырезаем чистую игру
68+
cropped = img[best_y : best_y + best_h, best_x : best_x + best_w]
69+
70+
print(
71+
f"Успешно вырезан внутренний экран: {best_w}x{best_h} в координатах x:{best_x}, y:{best_y}"
72+
)
73+
return cropped
74+
75+
76+
# TODO:
77+
PATH_DIR: Path = Path(r"C:\Users\ipetrash\Desktop\FF7 Screenshots\Карта Мира")
78+
NAME_DIR_CROPPED: str = "cropped-game-screen"
79+
80+
81+
for path in PATH_DIR.rglob("*.*"):
82+
if path.parent.name == NAME_DIR_CROPPED or path.suffix.lower() not in [
83+
".png",
84+
".jpg",
85+
".jpeg",
86+
]:
87+
continue
88+
89+
path_img_cropped: Path = path.parent / NAME_DIR_CROPPED / path.name
90+
if path_img_cropped.exists():
91+
continue
92+
93+
print()
94+
print(path)
95+
96+
img: cv2.typing.MatLike | None = cv2.imread(path)
97+
if img is None:
98+
continue
99+
100+
cropped: cv2.typing.MatLike | None = crop_inner_game_screen(img)
101+
if cropped is None:
102+
continue
103+
104+
path_img_cropped.parent.mkdir(parents=True, exist_ok=True)
105+
cv2.imwrite(path_img_cropped, cropped)
106+
107+
# TODO: remove
108+
# dsize = 1280, 720
109+
# cv2.imshow("original", cv2.resize(img, dsize))
110+
# cv2.imshow("cropped", cv2.resize(cropped, dsize))
111+
# cv2.resizeWindow("original", *dsize)
112+
# cv2.waitKey(0)
113+
# cv2.destroyAllWindows()
114+
#
115+
# TODO: remove
116+
# dsize = 1280, 720
117+
# cv2.imshow("thresh", cv2.resize(thresh, dsize))
118+
# cv2.imshow("contours", cv2.resize(img, dsize))
119+
# # cv2.imshow("cropped", cv2.resize(cropped, dsize))
120+
# cv2.resizeWindow("contours", *dsize)
121+
# cv2.waitKey(0)
122+
# cv2.destroyAllWindows()
123+
#
124+
# quit()
125+
126+
print()

0 commit comments

Comments
 (0)