2 Commits

Author SHA1 Message Date
c009a28f92 refactor: use opencv to calculate image phash 2023-10-03 16:38:11 +08:00
d5ccbd5a01 refactor: module structure 2023-10-03 15:05:03 +08:00
20 changed files with 548 additions and 532 deletions

View File

@ -1,3 +0,0 @@
from .common import DeviceAutoRoiSizes
from .t1 import DeviceAutoRoiSizesT1
from .t2 import DeviceAutoRoiSizesT2

View File

@ -1,7 +0,0 @@
from ..common import DeviceRoiSizes
class DeviceAutoRoiSizes(DeviceRoiSizes):
def __init__(self, w: int, h: int):
self.w = w
self.h = h

View File

@ -1,123 +0,0 @@
from .common import DeviceAutoRoiSizes
class DeviceAutoRoiSizesT1(DeviceAutoRoiSizes):
@property
def factor(self):
return (
((self.w / 16) * 9) / 720 if (self.w / self.h) < (16 / 9) else self.h / 720
)
@property
def w_mid(self):
return self.w / 2
@property
def h_mid(self):
return self.h / 2
@property
def top_bar(self):
return (0, 0, self.w, 50 * self.factor)
@property
def layout_area_h_mid(self):
return self.h / 2 + self.top_bar[3]
@property
def pfl_left_from_w_mid(self):
return 5 * self.factor
@property
def pfl_x(self):
return self.w_mid + self.pfl_left_from_w_mid
@property
def pfl_w(self):
return 76 * self.factor
@property
def pfl_h(self):
return 26 * self.factor
@property
def pure(self):
return (
self.pfl_x,
self.layout_area_h_mid + 110 * self.factor,
self.pfl_w,
self.pfl_h,
)
@property
def far(self):
return (
self.pfl_x,
self.pure[1] + self.pure[3] + 12 * self.factor,
self.pfl_w,
self.pfl_h,
)
@property
def lost(self):
return (
self.pfl_x,
self.far[1] + self.far[3] + 10 * self.factor,
self.pfl_w,
self.pfl_h,
)
@property
def score(self):
w = 280 * self.factor
h = 45 * self.factor
return (
self.w_mid - w / 2,
self.layout_area_h_mid - 75 * self.factor - h,
w,
h,
)
@property
def rating_class(self):
return (
self.w_mid - 610 * self.factor,
self.layout_area_h_mid - 180 * self.factor,
265 * self.factor,
35 * self.factor,
)
@property
def max_recall(self):
return (
self.w_mid - 465 * self.factor,
self.layout_area_h_mid - 215 * self.factor,
150 * self.factor,
35 * self.factor,
)
@property
def jacket(self):
return (
self.w_mid - 610 * self.factor,
self.layout_area_h_mid - 143 * self.factor,
375 * self.factor,
375 * self.factor,
)
@property
def clear_status(self):
w = 550 * self.factor
h = 60 * self.factor
return (
self.w_mid - w / 2,
self.layout_area_h_mid - 155 * self.factor - h,
w,
h,
)
@property
def partner_icon(self):
w = 90 * self.factor
h = 75 * self.factor
return (self.w_mid - w / 2, 0, w, h)

View File

@ -1,125 +0,0 @@
from .common import DeviceAutoRoiSizes
class DeviceAutoRoiSizesT2(DeviceAutoRoiSizes):
@property
def factor(self):
return (
((self.w / 16) * 9) / 1080
if (self.w / self.h) < (16 / 9)
else self.h / 1080
)
@property
def w_mid(self):
return self.w / 2
@property
def h_mid(self):
return self.h / 2
@property
def top_bar(self):
return (0, 0, self.w, 75 * self.factor)
@property
def layout_area_h_mid(self):
return self.h / 2 + self.top_bar[3]
@property
def pfl_mid_from_w_mid(self):
return 60 * self.factor
@property
def pfl_x(self):
return self.w_mid + 10 * self.factor
@property
def pfl_w(self):
return 100 * self.factor
@property
def pfl_h(self):
return 24 * self.factor
@property
def pure(self):
return (
self.pfl_x,
self.layout_area_h_mid + 175 * self.factor,
self.pfl_w,
self.pfl_h,
)
@property
def far(self):
return (
self.pfl_x,
self.pure[1] + self.pure[3] + 30 * self.factor,
self.pfl_w,
self.pfl_h,
)
@property
def lost(self):
return (
self.pfl_x,
self.far[1] + self.far[3] + 35 * self.factor,
self.pfl_w,
self.pfl_h,
)
@property
def score(self):
w = 420 * self.factor
h = 70 * self.factor
return (
self.w_mid - w / 2,
self.layout_area_h_mid - 110 * self.factor - h,
w,
h,
)
@property
def rating_class(self):
return (
max(0, self.w_mid - 965 * self.factor),
self.layout_area_h_mid - 330 * self.factor,
350 * self.factor,
110 * self.factor,
)
@property
def max_recall(self):
return (
self.w_mid - 625 * self.factor,
self.layout_area_h_mid - 275 * self.factor,
150 * self.factor,
50 * self.factor,
)
@property
def jacket(self):
return (
self.w_mid - 915 * self.factor,
self.layout_area_h_mid - 215 * self.factor,
565 * self.factor,
565 * self.factor,
)
@property
def clear_status(self):
w = 825 * self.factor
h = 90 * self.factor
return (
self.w_mid - w / 2,
self.layout_area_h_mid - 235 * self.factor - h,
w,
h,
)
@property
def partner_icon(self):
w = 135 * self.factor
h = 110 * self.factor
return (self.w_mid - w / 2, 0, w, h)

View File

@ -1 +0,0 @@
from .common import DeviceRoiExtractor

View File

@ -1,2 +0,0 @@
from .auto import *
from .common import DeviceRoiMasker

View File

@ -1,3 +0,0 @@
from .common import DeviceAutoRoiMasker
from .t1 import DeviceAutoRoiMaskerT1
from .t2 import DeviceAutoRoiMaskerT2

View File

@ -1,5 +0,0 @@
from ..common import DeviceRoiMasker
class DeviceAutoRoiMasker(DeviceRoiMasker):
...

View File

@ -1,123 +0,0 @@
import cv2
import numpy as np
from .common import DeviceAutoRoiMasker
GRAY_BGR_MIN = np.array([50] * 3, np.uint8)
GRAY_BGR_MAX = np.array([160] * 3, np.uint8)
WHITE_HSV_MIN = np.array([0, 0, 240], np.uint8)
WHITE_HSV_MAX = np.array([179, 10, 255], np.uint8)
PST_HSV_MIN = np.array([100, 50, 80], np.uint8)
PST_HSV_MAX = np.array([100, 255, 255], np.uint8)
PRS_HSV_MIN = np.array([43, 40, 75], np.uint8)
PRS_HSV_MAX = np.array([50, 155, 190], np.uint8)
FTR_HSV_MIN = np.array([149, 30, 0], np.uint8)
FTR_HSV_MAX = np.array([155, 181, 150], np.uint8)
BYD_HSV_MIN = np.array([170, 50, 50], np.uint8)
BYD_HSV_MAX = np.array([179, 210, 198], np.uint8)
TRACK_LOST_HSV_MIN = np.array([170, 75, 90], np.uint8)
TRACK_LOST_HSV_MAX = np.array([175, 170, 160], np.uint8)
TRACK_COMPLETE_HSV_MIN = np.array([140, 0, 50], np.uint8)
TRACK_COMPLETE_HSV_MAX = np.array([145, 50, 130], np.uint8)
FULL_RECALL_HSV_MIN = np.array([140, 60, 80], np.uint8)
FULL_RECALL_HSV_MAX = np.array([150, 130, 145], np.uint8)
PURE_MEMORY_HSV_MIN = np.array([90, 70, 80], np.uint8)
PURE_MEMORY_HSV_MAX = np.array([110, 200, 175], np.uint8)
class DeviceAutoRoiMaskerT1(DeviceAutoRoiMasker):
@classmethod
def gray(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
bgr_value_equal_mask = np.max(roi_bgr, axis=2) - np.min(roi_bgr, axis=2) <= 5
img_bgr = roi_bgr.copy()
img_bgr[~bgr_value_equal_mask] = np.array([0, 0, 0], roi_bgr.dtype)
img_bgr = cv2.erode(img_bgr, cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2)))
img_bgr = cv2.dilate(img_bgr, cv2.getStructuringElement(cv2.MORPH_RECT, (1, 1)))
return cv2.inRange(img_bgr, GRAY_BGR_MIN, GRAY_BGR_MAX)
@classmethod
def pure(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cls.gray(roi_bgr)
@classmethod
def far(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cls.gray(roi_bgr)
@classmethod
def lost(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cls.gray(roi_bgr)
@classmethod
def score(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), WHITE_HSV_MIN, WHITE_HSV_MAX
)
@classmethod
def rating_class_pst(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), PST_HSV_MIN, PST_HSV_MAX
)
@classmethod
def rating_class_prs(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), PRS_HSV_MIN, PRS_HSV_MAX
)
@classmethod
def rating_class_ftr(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), FTR_HSV_MIN, FTR_HSV_MAX
)
@classmethod
def rating_class_byd(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), BYD_HSV_MIN, BYD_HSV_MAX
)
@classmethod
def max_recall(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cls.gray(roi_bgr)
@classmethod
def clear_status_track_lost(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
TRACK_LOST_HSV_MIN,
TRACK_LOST_HSV_MAX,
)
@classmethod
def clear_status_track_complete(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
TRACK_COMPLETE_HSV_MIN,
TRACK_COMPLETE_HSV_MAX,
)
@classmethod
def clear_status_full_recall(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
FULL_RECALL_HSV_MIN,
FULL_RECALL_HSV_MAX,
)
@classmethod
def clear_status_pure_memory(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
PURE_MEMORY_HSV_MIN,
PURE_MEMORY_HSV_MAX,
)

View File

@ -1,128 +0,0 @@
import cv2
import numpy as np
from .common import DeviceAutoRoiMasker
PFL_HSV_MIN = np.array([0, 0, 248], np.uint8)
PFL_HSV_MAX = np.array([179, 10, 255], np.uint8)
WHITE_HSV_MIN = np.array([0, 0, 240], np.uint8)
WHITE_HSV_MAX = np.array([179, 10, 255], np.uint8)
PST_HSV_MIN = np.array([100, 50, 80], np.uint8)
PST_HSV_MAX = np.array([100, 255, 255], np.uint8)
PRS_HSV_MIN = np.array([43, 40, 75], np.uint8)
PRS_HSV_MAX = np.array([50, 155, 190], np.uint8)
FTR_HSV_MIN = np.array([149, 30, 0], np.uint8)
FTR_HSV_MAX = np.array([155, 181, 150], np.uint8)
BYD_HSV_MIN = np.array([170, 50, 50], np.uint8)
BYD_HSV_MAX = np.array([179, 210, 198], np.uint8)
MAX_RECALL_HSV_MIN = np.array([125, 0, 0], np.uint8)
MAX_RECALL_HSV_MAX = np.array([130, 100, 150], np.uint8)
TRACK_LOST_HSV_MIN = np.array([170, 75, 90], np.uint8)
TRACK_LOST_HSV_MAX = np.array([175, 170, 160], np.uint8)
TRACK_COMPLETE_HSV_MIN = np.array([140, 0, 50], np.uint8)
TRACK_COMPLETE_HSV_MAX = np.array([145, 50, 130], np.uint8)
FULL_RECALL_HSV_MIN = np.array([140, 60, 80], np.uint8)
FULL_RECALL_HSV_MAX = np.array([150, 130, 145], np.uint8)
PURE_MEMORY_HSV_MIN = np.array([90, 70, 80], np.uint8)
PURE_MEMORY_HSV_MAX = np.array([110, 200, 175], np.uint8)
class DeviceAutoRoiMaskerT2(DeviceAutoRoiMasker):
@classmethod
def pfl(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), PFL_HSV_MIN, PFL_HSV_MAX
)
@classmethod
def pure(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cls.pfl(roi_bgr)
@classmethod
def far(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cls.pfl(roi_bgr)
@classmethod
def lost(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cls.pfl(roi_bgr)
@classmethod
def score(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), WHITE_HSV_MIN, WHITE_HSV_MAX
)
@classmethod
def rating_class_pst(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), PST_HSV_MIN, PST_HSV_MAX
)
@classmethod
def rating_class_prs(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), PRS_HSV_MIN, PRS_HSV_MAX
)
@classmethod
def rating_class_ftr(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), FTR_HSV_MIN, FTR_HSV_MAX
)
@classmethod
def rating_class_byd(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), BYD_HSV_MIN, BYD_HSV_MAX
)
@classmethod
def max_recall(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
MAX_RECALL_HSV_MIN,
MAX_RECALL_HSV_MAX,
)
@classmethod
def clear_status_track_lost(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
TRACK_LOST_HSV_MIN,
TRACK_LOST_HSV_MAX,
)
@classmethod
def clear_status_track_complete(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
TRACK_COMPLETE_HSV_MIN,
TRACK_COMPLETE_HSV_MAX,
)
@classmethod
def clear_status_full_recall(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
FULL_RECALL_HSV_MIN,
FULL_RECALL_HSV_MAX,
)
@classmethod
def clear_status_pure_memory(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
PURE_MEMORY_HSV_MIN,
PURE_MEMORY_HSV_MAX,
)

View File

@ -0,0 +1,255 @@
from .common import DeviceRois
__all__ = ["DeviceRoisAuto", "DeviceRoisAutoT1", "DeviceRoisAutoT2"]
class DeviceRoisAuto(DeviceRois):
def __init__(self, w: int, h: int):
self.w = w
self.h = h
class DeviceRoisAutoT1(DeviceRoisAuto):
@property
def factor(self):
return (
((self.w / 16) * 9) / 720 if (self.w / self.h) < (16 / 9) else self.h / 720
)
@property
def w_mid(self):
return self.w / 2
@property
def h_mid(self):
return self.h / 2
@property
def top_bar(self):
return (0, 0, self.w, 50 * self.factor)
@property
def layout_area_h_mid(self):
return self.h / 2 + self.top_bar[3]
@property
def pfl_left_from_w_mid(self):
return 5 * self.factor
@property
def pfl_x(self):
return self.w_mid + self.pfl_left_from_w_mid
@property
def pfl_w(self):
return 76 * self.factor
@property
def pfl_h(self):
return 26 * self.factor
@property
def pure(self):
return (
self.pfl_x,
self.layout_area_h_mid + 110 * self.factor,
self.pfl_w,
self.pfl_h,
)
@property
def far(self):
return (
self.pfl_x,
self.pure[1] + self.pure[3] + 12 * self.factor,
self.pfl_w,
self.pfl_h,
)
@property
def lost(self):
return (
self.pfl_x,
self.far[1] + self.far[3] + 10 * self.factor,
self.pfl_w,
self.pfl_h,
)
@property
def score(self):
w = 280 * self.factor
h = 45 * self.factor
return (
self.w_mid - w / 2,
self.layout_area_h_mid - 75 * self.factor - h,
w,
h,
)
@property
def rating_class(self):
return (
self.w_mid - 610 * self.factor,
self.layout_area_h_mid - 180 * self.factor,
265 * self.factor,
35 * self.factor,
)
@property
def max_recall(self):
return (
self.w_mid - 465 * self.factor,
self.layout_area_h_mid - 215 * self.factor,
150 * self.factor,
35 * self.factor,
)
@property
def jacket(self):
return (
self.w_mid - 610 * self.factor,
self.layout_area_h_mid - 143 * self.factor,
375 * self.factor,
375 * self.factor,
)
@property
def clear_status(self):
w = 550 * self.factor
h = 60 * self.factor
return (
self.w_mid - w / 2,
self.layout_area_h_mid - 155 * self.factor - h,
w,
h,
)
@property
def partner_icon(self):
w = 90 * self.factor
h = 75 * self.factor
return (self.w_mid - w / 2, 0, w, h)
class DeviceRoisAutoT2(DeviceRoisAuto):
@property
def factor(self):
return (
((self.w / 16) * 9) / 1080
if (self.w / self.h) < (16 / 9)
else self.h / 1080
)
@property
def w_mid(self):
return self.w / 2
@property
def h_mid(self):
return self.h / 2
@property
def top_bar(self):
return (0, 0, self.w, 75 * self.factor)
@property
def layout_area_h_mid(self):
return self.h / 2 + self.top_bar[3]
@property
def pfl_mid_from_w_mid(self):
return 60 * self.factor
@property
def pfl_x(self):
return self.w_mid + 10 * self.factor
@property
def pfl_w(self):
return 100 * self.factor
@property
def pfl_h(self):
return 24 * self.factor
@property
def pure(self):
return (
self.pfl_x,
self.layout_area_h_mid + 175 * self.factor,
self.pfl_w,
self.pfl_h,
)
@property
def far(self):
return (
self.pfl_x,
self.pure[1] + self.pure[3] + 30 * self.factor,
self.pfl_w,
self.pfl_h,
)
@property
def lost(self):
return (
self.pfl_x,
self.far[1] + self.far[3] + 35 * self.factor,
self.pfl_w,
self.pfl_h,
)
@property
def score(self):
w = 420 * self.factor
h = 70 * self.factor
return (
self.w_mid - w / 2,
self.layout_area_h_mid - 110 * self.factor - h,
w,
h,
)
@property
def rating_class(self):
return (
max(0, self.w_mid - 965 * self.factor),
self.layout_area_h_mid - 330 * self.factor,
350 * self.factor,
110 * self.factor,
)
@property
def max_recall(self):
return (
self.w_mid - 625 * self.factor,
self.layout_area_h_mid - 275 * self.factor,
150 * self.factor,
50 * self.factor,
)
@property
def jacket(self):
return (
self.w_mid - 915 * self.factor,
self.layout_area_h_mid - 215 * self.factor,
565 * self.factor,
565 * self.factor,
)
@property
def clear_status(self):
w = 825 * self.factor
h = 90 * self.factor
return (
self.w_mid - w / 2,
self.layout_area_h_mid - 235 * self.factor - h,
w,
h,
)
@property
def partner_icon(self):
w = 135 * self.factor
h = 110 * self.factor
return (self.w_mid - w / 2, 0, w, h)

View File

@ -3,7 +3,7 @@ from typing import Tuple
Rect = Tuple[int, int, int, int]
class DeviceRoiSizes:
class DeviceRois:
pure: Rect
far: Rect
lost: Rect

View File

@ -0,0 +1 @@
from .common import DeviceRoisExtractor

View File

@ -1,11 +1,11 @@
import cv2
from ....crop import crop_xywh
from ..definitions.common import DeviceRoiSizes
from ..definition.common import DeviceRois
class DeviceRoiExtractor:
def __init__(self, img: cv2.Mat, sizes: DeviceRoiSizes):
class DeviceRoisExtractor:
def __init__(self, img: cv2.Mat, sizes: DeviceRois):
self.img = img
self.sizes = sizes

View File

@ -0,0 +1,2 @@
from .auto import *
from .common import DeviceRoisMasker

View File

@ -0,0 +1,254 @@
import cv2
import numpy as np
from .common import DeviceRoisMasker
class DeviceRoisMaskerAuto(DeviceRoisMasker):
...
class DeviceRoisMaskerAutoT1(DeviceRoisMaskerAuto):
GRAY_BGR_MIN = np.array([50] * 3, np.uint8)
GRAY_BGR_MAX = np.array([160] * 3, np.uint8)
WHITE_HSV_MIN = np.array([0, 0, 240], np.uint8)
WHITE_HSV_MAX = np.array([179, 10, 255], np.uint8)
PST_HSV_MIN = np.array([100, 50, 80], np.uint8)
PST_HSV_MAX = np.array([100, 255, 255], np.uint8)
PRS_HSV_MIN = np.array([43, 40, 75], np.uint8)
PRS_HSV_MAX = np.array([50, 155, 190], np.uint8)
FTR_HSV_MIN = np.array([149, 30, 0], np.uint8)
FTR_HSV_MAX = np.array([155, 181, 150], np.uint8)
BYD_HSV_MIN = np.array([170, 50, 50], np.uint8)
BYD_HSV_MAX = np.array([179, 210, 198], np.uint8)
TRACK_LOST_HSV_MIN = np.array([170, 75, 90], np.uint8)
TRACK_LOST_HSV_MAX = np.array([175, 170, 160], np.uint8)
TRACK_COMPLETE_HSV_MIN = np.array([140, 0, 50], np.uint8)
TRACK_COMPLETE_HSV_MAX = np.array([145, 50, 130], np.uint8)
FULL_RECALL_HSV_MIN = np.array([140, 60, 80], np.uint8)
FULL_RECALL_HSV_MAX = np.array([150, 130, 145], np.uint8)
PURE_MEMORY_HSV_MIN = np.array([90, 70, 80], np.uint8)
PURE_MEMORY_HSV_MAX = np.array([110, 200, 175], np.uint8)
@classmethod
def gray(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
bgr_value_equal_mask = np.max(roi_bgr, axis=2) - np.min(roi_bgr, axis=2) <= 5
img_bgr = roi_bgr.copy()
img_bgr[~bgr_value_equal_mask] = np.array([0, 0, 0], roi_bgr.dtype)
img_bgr = cv2.erode(img_bgr, cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2)))
img_bgr = cv2.dilate(img_bgr, cv2.getStructuringElement(cv2.MORPH_RECT, (1, 1)))
return cv2.inRange(img_bgr, cls.GRAY_BGR_MIN, cls.GRAY_BGR_MAX)
@classmethod
def pure(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cls.gray(roi_bgr)
@classmethod
def far(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cls.gray(roi_bgr)
@classmethod
def lost(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cls.gray(roi_bgr)
@classmethod
def score(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
cls.WHITE_HSV_MIN,
cls.WHITE_HSV_MAX,
)
@classmethod
def rating_class_pst(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), cls.PST_HSV_MIN, cls.PST_HSV_MAX
)
@classmethod
def rating_class_prs(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), cls.PRS_HSV_MIN, cls.PRS_HSV_MAX
)
@classmethod
def rating_class_ftr(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), cls.FTR_HSV_MIN, cls.FTR_HSV_MAX
)
@classmethod
def rating_class_byd(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), cls.BYD_HSV_MIN, cls.BYD_HSV_MAX
)
@classmethod
def max_recall(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cls.gray(roi_bgr)
@classmethod
def clear_status_track_lost(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
cls.TRACK_LOST_HSV_MIN,
cls.TRACK_LOST_HSV_MAX,
)
@classmethod
def clear_status_track_complete(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
cls.TRACK_COMPLETE_HSV_MIN,
cls.TRACK_COMPLETE_HSV_MAX,
)
@classmethod
def clear_status_full_recall(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
cls.FULL_RECALL_HSV_MIN,
cls.FULL_RECALL_HSV_MAX,
)
@classmethod
def clear_status_pure_memory(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
cls.PURE_MEMORY_HSV_MIN,
cls.PURE_MEMORY_HSV_MAX,
)
class DeviceRoisMaskerAutoT2(DeviceRoisMaskerAuto):
PFL_HSV_MIN = np.array([0, 0, 248], np.uint8)
PFL_HSV_MAX = np.array([179, 10, 255], np.uint8)
WHITE_HSV_MIN = np.array([0, 0, 240], np.uint8)
WHITE_HSV_MAX = np.array([179, 10, 255], np.uint8)
PST_HSV_MIN = np.array([100, 50, 80], np.uint8)
PST_HSV_MAX = np.array([100, 255, 255], np.uint8)
PRS_HSV_MIN = np.array([43, 40, 75], np.uint8)
PRS_HSV_MAX = np.array([50, 155, 190], np.uint8)
FTR_HSV_MIN = np.array([149, 30, 0], np.uint8)
FTR_HSV_MAX = np.array([155, 181, 150], np.uint8)
BYD_HSV_MIN = np.array([170, 50, 50], np.uint8)
BYD_HSV_MAX = np.array([179, 210, 198], np.uint8)
MAX_RECALL_HSV_MIN = np.array([125, 0, 0], np.uint8)
MAX_RECALL_HSV_MAX = np.array([130, 100, 150], np.uint8)
TRACK_LOST_HSV_MIN = np.array([170, 75, 90], np.uint8)
TRACK_LOST_HSV_MAX = np.array([175, 170, 160], np.uint8)
TRACK_COMPLETE_HSV_MIN = np.array([140, 0, 50], np.uint8)
TRACK_COMPLETE_HSV_MAX = np.array([145, 50, 130], np.uint8)
FULL_RECALL_HSV_MIN = np.array([140, 60, 80], np.uint8)
FULL_RECALL_HSV_MAX = np.array([150, 130, 145], np.uint8)
PURE_MEMORY_HSV_MIN = np.array([90, 70, 80], np.uint8)
PURE_MEMORY_HSV_MAX = np.array([110, 200, 175], np.uint8)
@classmethod
def pfl(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), cls.PFL_HSV_MIN, cls.PFL_HSV_MAX
)
@classmethod
def pure(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cls.pfl(roi_bgr)
@classmethod
def far(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cls.pfl(roi_bgr)
@classmethod
def lost(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cls.pfl(roi_bgr)
@classmethod
def score(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
cls.WHITE_HSV_MIN,
cls.WHITE_HSV_MAX,
)
@classmethod
def rating_class_pst(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), cls.PST_HSV_MIN, cls.PST_HSV_MAX
)
@classmethod
def rating_class_prs(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), cls.PRS_HSV_MIN, cls.PRS_HSV_MAX
)
@classmethod
def rating_class_ftr(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), cls.FTR_HSV_MIN, cls.FTR_HSV_MAX
)
@classmethod
def rating_class_byd(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV), cls.BYD_HSV_MIN, cls.BYD_HSV_MAX
)
@classmethod
def max_recall(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
cls.MAX_RECALL_HSV_MIN,
cls.MAX_RECALL_HSV_MAX,
)
@classmethod
def clear_status_track_lost(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
cls.TRACK_LOST_HSV_MIN,
cls.TRACK_LOST_HSV_MAX,
)
@classmethod
def clear_status_track_complete(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
cls.TRACK_COMPLETE_HSV_MIN,
cls.TRACK_COMPLETE_HSV_MAX,
)
@classmethod
def clear_status_full_recall(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
cls.FULL_RECALL_HSV_MIN,
cls.FULL_RECALL_HSV_MAX,
)
@classmethod
def clear_status_pure_memory(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
return cv2.inRange(
cv2.cvtColor(roi_bgr, cv2.COLOR_BGR2HSV),
cls.PURE_MEMORY_HSV_MIN,
cls.PURE_MEMORY_HSV_MAX,
)

View File

@ -1,7 +1,7 @@
import cv2
class DeviceRoiMasker:
class DeviceRoisMasker:
@classmethod
def pure(cls, roi_bgr: cv2.Mat) -> cv2.Mat:
raise NotImplementedError()

View File

@ -1,8 +1,32 @@
import sqlite3
import imagehash
import cv2
import numpy as np
from PIL import Image
def phash_opencv(img_gray, hash_size=8, highfreq_factor=4):
# type: (cv2.Mat | np.ndarray, int, int) -> np.ndarray
"""
Perceptual Hash computation.
Implementation follows http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html
Adapted from `imagehash.phash`, pure opencv implementation
The result is slightly different from `imagehash.phash`.
"""
if hash_size < 2:
raise ValueError("Hash size must be greater than or equal to 2")
img_size = hash_size * highfreq_factor
image = cv2.resize(img_gray, (img_size, img_size), interpolation=cv2.INTER_LANCZOS4)
image = np.float32(image)
dct = cv2.dct(image, flags=cv2.DCT_ROWS)
dct = cv2.dct(image)
dctlowfreq = dct[:hash_size, :hash_size]
med = np.median(dctlowfreq)
diff = dctlowfreq > med
return diff
def hamming_distance_sql_function(user_input, db_entry) -> int:
@ -46,8 +70,8 @@ class ImagePHashDatabase:
self.hashes_head = [h[: self.hashes_slice_size] for h in self.hashes]
self.hashes_tail = [h[-self.hashes_slice_size :] for h in self.hashes]
def lookup_hash(self, image_hash: imagehash.ImageHash, *, limit: int = 5):
image_hash = image_hash.hash.flatten()
def lookup_hash(self, image_hash: np.ndarray, *, limit: int = 5):
image_hash = image_hash.flatten()
# image_hash_head = image_hash[: self.hashes_slice_size]
# image_hash_tail = image_hash[-self.hashes_slice_size :]
# head_xor_results = [image_hash_head ^ h for h in self.hashes]
@ -58,8 +82,8 @@ class ImagePHashDatabase:
]
return sorted(xor_results, key=lambda r: r[1])[:limit]
def lookup_image(self, pil_image: Image.Image):
image_hash = imagehash.phash(
pil_image, hash_size=self.hash_size, highfreq_factor=self.highfreq_factor
def lookup_image(self, img_gray: cv2.Mat):
image_hash = phash_opencv(
img_gray, hash_size=self.hash_size, highfreq_factor=self.highfreq_factor
)
return self.lookup_hash(image_hash)[0]