-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
265 additions
and
12 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
import os.path | ||
import re | ||
import sys | ||
from functools import lru_cache | ||
from typing import Any | ||
from typing import Callable | ||
from typing import Union | ||
|
||
import mss | ||
import numpy as np | ||
import PIL.Image | ||
import PIL.ImageGrab | ||
import tapper | ||
from mss.base import MSSBase | ||
from numpy import ndarray | ||
from tapper.helper._util import image_fuzz | ||
from tapper.helper.model_types import BboxT | ||
from tapper.helper.model_types import ImagePathT | ||
from tapper.helper.model_types import ImagePixelMatrixT | ||
from tapper.helper.model_types import ImageT | ||
from tapper.helper.model_types import PixelColorT | ||
from tapper.helper.model_types import XyCoordsT | ||
from tapper.model import constants | ||
|
||
|
||
def find_fuzzy_cv2(target: ndarray, outer: ndarray) -> tuple[float, XyCoordsT]: | ||
import cv2 | ||
|
||
comparison = cv2.matchTemplate(outer, target, cv2.TM_CCORR_NORMED) | ||
_, max_val, _, max_loc = cv2.minMaxLoc(comparison) | ||
return max_val, max_loc # type: ignore | ||
|
||
|
||
mss_instance: MSSBase | ||
|
||
|
||
def get_mss() -> MSSBase: | ||
global mss_instance | ||
mss_instance = mss.mss() | ||
return mss_instance | ||
|
||
|
||
@lru_cache(maxsize=5) | ||
def from_path(pathlike: ImagePathT) -> ImagePixelMatrixT: | ||
if isinstance(pathlike, str): | ||
pathlike = os.path.abspath(pathlike) | ||
pil_img = PIL.Image.open(pathlike).convert("RGB") | ||
return np.asarray(pil_img) | ||
|
||
|
||
def to_pixel_matrix(image: ImageT | None) -> ImagePixelMatrixT | None: | ||
if image is None: | ||
return None | ||
elif isinstance(image, ndarray): | ||
return image | ||
elif isinstance(image, (str, bytes, os.PathLike)): | ||
return from_path(os.path.abspath(image)) | ||
else: | ||
raise TypeError(f"Unexpected type, {type(image)} of {image}") | ||
|
||
|
||
def get_image_size(image: ImagePixelMatrixT) -> tuple[int, int]: | ||
return image.shape[1], image.shape[0] | ||
|
||
|
||
def get_screenshot_if_none_and_cut( | ||
maybe_image: ImagePixelMatrixT | None, bbox: BboxT | None | ||
) -> ImagePixelMatrixT: | ||
if maybe_image is not None: | ||
if bbox: | ||
return maybe_image[bbox[1] : bbox[3], bbox[0] : bbox[2]] | ||
return maybe_image | ||
if bbox is not None: | ||
try: | ||
sct = get_mss().grab(bbox) | ||
except Exception as e: | ||
raise e | ||
else: | ||
sct = get_mss().grab(get_mss().monitors[0]) | ||
pil_rgb = PIL.Image.frombytes("RGB", sct.size, sct.bgra, "raw", "BGRX") | ||
return np.asarray(pil_rgb) | ||
|
||
|
||
def check_bbox_smaller_or_eq( | ||
image: ImagePixelMatrixT | None, bbox: BboxT | None | ||
) -> None: | ||
if image is None or bbox is None: | ||
return | ||
bbox_x = abs(bbox[2] - bbox[0]) | ||
bbox_y = abs(bbox[3] - bbox[1]) | ||
image_x, image_y = get_image_size(image) | ||
if bbox_x > image_x or bbox_y > image_y: | ||
raise ValueError( | ||
f"Bbox should NOT be bigger, but got {bbox_x}x{bbox_y} vs image {image_x}x{image_y}" | ||
) | ||
|
||
|
||
def check_bbox_bigger_or_eq( | ||
image: ImagePixelMatrixT | None, bbox: BboxT | None | ||
) -> None: | ||
if image is None or bbox is None: | ||
return | ||
bbox_x = abs(bbox[2] - bbox[0]) | ||
bbox_y = abs(bbox[3] - bbox[1]) | ||
image_x, image_y = get_image_size(image) | ||
if bbox_x < image_x or bbox_y < image_y: | ||
raise ValueError( | ||
f"Bbox should NOT be smaller, but got {bbox_x}x{bbox_y} vs image {image_x}x{image_y}" | ||
) | ||
|
||
|
||
def get_start_coords( | ||
outer: ndarray | None, | ||
bbox_or_coords: BboxT | XyCoordsT | None, | ||
) -> XyCoordsT: | ||
if bbox_or_coords is not None: | ||
return bbox_or_coords[0], bbox_or_coords[1] | ||
if outer is None and sys.platform == constants.OS.win32: | ||
return win32_coords_start() | ||
return 0, 0 | ||
|
||
|
||
def win32_coords_start() -> XyCoordsT: | ||
"""Win32 may start with negative coords when multiscreen.""" | ||
import winput | ||
from win32api import GetSystemMetrics | ||
|
||
winput.set_DPI_aware(per_monitor=True) | ||
x = GetSystemMetrics(76) | ||
y = GetSystemMetrics(77) | ||
return x, y | ||
|
||
|
||
def target_to_image(target: ImageT, bbox: BboxT | None) -> ImagePixelMatrixT: | ||
"""Transform and verify target from API input to workable image-array.""" | ||
target_image = to_pixel_matrix(target) | ||
assert target_image is not None # for mypy | ||
if bbox is not None: | ||
bbox_x = abs(bbox[2] - bbox[0]) | ||
bbox_y = abs(bbox[3] - bbox[1]) | ||
image_x, image_y = get_image_size(target_image) | ||
if bbox_x < image_x or bbox_y < image_y: | ||
raise ValueError( | ||
f"Bbox should NOT be smaller than target, " | ||
f"but got {bbox_x}x{bbox_y} vs target {image_x}x{image_y}" | ||
) | ||
return target_image | ||
|
||
|
||
def outer_to_image(outer_or_path_maybe: ImageT | None, bbox: BboxT | None) -> ImagePixelMatrixT: | ||
"""Transform and verify target from API input (or screenshot) to workable image-array.""" | ||
outer_maybe = to_pixel_matrix(outer_or_path_maybe) | ||
if outer_maybe is not None and bbox is not None: | ||
bbox_x = abs(bbox[2] - bbox[0]) | ||
bbox_y = abs(bbox[3] - bbox[1]) | ||
image_x, image_y = get_image_size(outer_maybe) | ||
if bbox_x > image_x or bbox_y > image_y: | ||
raise ValueError( | ||
f"Bbox should NOT be bigger than outer, " | ||
f"but got {bbox_x}x{bbox_y} vs outer {image_x}x{image_y}" | ||
) | ||
return get_screenshot_if_none_and_cut(outer_maybe, bbox) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import os.path | ||
import re | ||
import sys | ||
from functools import lru_cache | ||
from lib2to3.pytree import convert | ||
from sys import prefix | ||
from typing import Any | ||
from typing import Callable | ||
from typing import Union | ||
|
||
import mss | ||
import numpy as np | ||
import PIL.Image | ||
import PIL.ImageGrab | ||
import tapper | ||
from mss.base import MSSBase | ||
from numpy import ndarray | ||
from tapper.helper._util.image import base | ||
from tapper.helper.model_types import BboxT | ||
from tapper.helper.model_types import ImagePathT | ||
from tapper.helper.model_types import ImagePixelMatrixT | ||
from tapper.helper.model_types import ImageT | ||
from tapper.helper.model_types import PixelColorT | ||
from tapper.helper.model_types import XyCoordsT | ||
from tapper.model import constants | ||
|
||
|
||
def find_raw( | ||
target: ImagePixelMatrixT, | ||
outer: ImagePixelMatrixT, | ||
) -> tuple[float, XyCoordsT]: | ||
"""Doesn't account for the starting coords of the search.""" | ||
confidence, coords = base.find_fuzzy_cv2(target, outer) | ||
target_size_x, target_size_y = base.get_image_size(target) | ||
return confidence, (coords[0] + target_size_x // 2, coords[1] + target_size_y // 2) | ||
|
||
|
||
def api_find_raw( | ||
image: ImagePixelMatrixT, | ||
bbox: BboxT | None, | ||
outer_maybe: ImagePixelMatrixT | None = None, | ||
) -> tuple[float, XyCoordsT]: | ||
x_start, y_start = base.get_start_coords(outer_maybe, bbox) | ||
outer = base.outer_to_image(outer_maybe, bbox) | ||
confidence, xy = find_raw(image, outer) | ||
return confidence, (x_start + xy[0], y_start + xy[1]) | ||
|
||
|
||
def find( | ||
target: ImageT, | ||
outer: ImageT, | ||
precision: float, | ||
) -> XyCoordsT | None: | ||
"""Doesn't account for the starting coords of the search.""" | ||
confidence, xy = find_raw(target, outer) | ||
if confidence < precision: | ||
return None | ||
return xy | ||
|
||
|
||
def api_find( | ||
target: ImageT, | ||
bbox: BboxT | None, | ||
outer_or_path_maybe: ImageT | None = None, | ||
precision: float = 1.0, | ||
) -> XyCoordsT | None: | ||
if target is None: | ||
raise ValueError("image_find nees something to search for.") | ||
target_image = base.target_to_image(target, bbox) | ||
outer = base.outer_to_image(outer_or_path_maybe, bbox) | ||
found = find(target_image, outer, precision) | ||
if found is None: | ||
return None | ||
x_start, y_start = base.get_start_coords(outer_or_path_maybe, bbox) | ||
return x_start + found[0], y_start + found[1] | ||
|
||
|
||
def find_one_of() -> None: | ||
pass | ||
|
||
|
||
def wait_for() -> None: | ||
pass | ||
|
||
|
||
def wait_for_one_of() -> None: | ||
pass |
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
|
||
class TestSnip: | ||
def test_simplest(self) -> None: | ||
pass | ||
|
||
def test_saved_image_same_as_on_disk(self) -> None: | ||
pass |