ok-script 0.0.86__tar.gz → 0.0.96__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ok-script-0.0.86 → ok-script-0.0.96}/PKG-INFO +1 -1
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/OK.py +26 -2
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/BaseCaptureMethod.py +0 -10
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/HwndWindow.py +2 -4
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/adb/DeviceManager.py +5 -5
- ok-script-0.0.96/ok/capture/adb/WindowsCaptureFactory.py +42 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/windows/BitBltCaptureMethod.py +12 -7
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/windows/WindowsGraphicsCaptureMethod.py +73 -60
- ok-script-0.0.96/ok/capture/windows/dump.py +81 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/color/Color.py +34 -0
- ok-script-0.0.96/ok/feature/CompressCoco.py +75 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/feature/Feature.py +11 -5
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/feature/FeatureSet.py +132 -75
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/firebase/Analytics.py +2 -2
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/App.py +10 -9
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/MainWindow.py +4 -16
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/StartController.py +2 -2
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/debug/DebugTab.py +20 -18
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/debug/LogWindow.py +33 -31
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/resources.py +433 -432
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/start/StartCard.py +9 -0
- ok-script-0.0.96/ok/gui/tasks/OneTimeTaskTab.py +20 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/TaskOpButton.py +2 -8
- ok-script-0.0.86/ok/gui/tasks/OneTimeTaskTab.py → ok-script-0.0.96/ok/gui/tasks/TaskTab.py +5 -7
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/TriggerTaskTab.py +5 -2
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/interaction/BaseInteraction.py +6 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/interaction/Win32Interaction.py +18 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/ocr/OCR.py +3 -3
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/task/BaseTask.py +12 -15
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/task/ExecutorOperation.py +20 -10
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/task/TaskExecutor.py +44 -45
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/task/TriggerTask.py +0 -1
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/update/GithubMultiDownloader.py +48 -11
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/update/Updater.py +49 -28
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/util/Handler.py +1 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok_script.egg-info/PKG-INFO +1 -1
- {ok-script-0.0.86 → ok-script-0.0.96}/ok_script.egg-info/SOURCES.txt +2 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/setup.py +1 -1
- ok-script-0.0.86/ok/capture/adb/WindowsCaptureFactory.py +0 -41
- ok-script-0.0.86/ok/capture/windows/dump.py +0 -31
- {ok-script-0.0.86 → ok-script-0.0.96}/README.md +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/alas/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/alas/emulator_base.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/alas/emulator_windows.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/alas/platform_base.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/alas/platform_windows.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/alas/utils.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/adb/ADBCaptureMethod.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/adb/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/adb/bin/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/image/ImageCaptureMethod.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/image/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/windows/BaseWindowsCaptureMethod.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/windows/DesktopDuplicationCaptureMethod.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/windows/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/windows/d3d11.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/windows/utils.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/windows/window.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/color/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/config/Config.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/config/InfoDict.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/config/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/feature/Box.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/feature/FindFeature.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/feature/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/firebase/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/Communicate.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/MessageWindow.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/TabTitles.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/about/AboutTab.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/about/HttpTextEdit.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/about/VersionCard.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/about/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/common/OKIcon.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/common/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/common/config.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/common/style_sheet.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/debug/AspectRatioWidget.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/debug/FrameWidget.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/debug/InfoWidget.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/debug/Screenshot.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/debug/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/i18n/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/i18n/path.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/i18n/update.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/icon/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/icon/icon.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/overlay/OverlayWindow.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/overlay/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/qss/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/settings/SettingTab.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/settings/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/start/SelectCaptureListView.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/start/StartTab.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/start/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/ConfigItemFactory.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/ConfigLabelAndWidget.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/LabelAndDoubleSpinBox.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/LabelAndDropDown.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/LabelAndLineEdit.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/LabelAndList.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/LabelAndSpinBox.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/LabelAndSwitchButton.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/LabelAndWidget.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/ModifyListDialog.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/ModifyListItem.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/StartButton.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/TaskCard.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/TooltipTableWidget.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/util/Alert.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/util/InitWorker.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/util/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/Card.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/EmptyCard.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/FramelessDialog.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/ListTableWidgetItem.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/NumericTableWidgetItem.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/StartLoadingDialog.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/StatusBar.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/Tab.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/TabWidget.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/UpdateConfigWidgetItem.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/YesNonWidgetItem.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/interaction/ADBInteraction.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/interaction/DoNothingInteraction.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/interaction/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/logging/Logger.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/logging/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/ocr/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Foundation/Collections.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Foundation/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Globalization/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Graphics/Capture/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Graphics/DirectX/Direct3D11/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Graphics/DirectX/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Graphics/Imaging/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Graphics/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Media/Ocr/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Media/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Security/Cryptography/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Security/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Storage/Streams/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Storage/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/delegate.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/export.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/idldsl.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/inspectable.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/roapi.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/types.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/winstring.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/save/BlackBarProcessor.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/save/PostProcessor.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/save/SaveByInterval.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/save/SaveByKeyPress.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/save/SaveMethodBase.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/save/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/scene/FeatureScene.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/scene/Scene.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/scene/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/stats/StreamStats.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/stats/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/task/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/update/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/update/gen_md5.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/util/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/util/exit_event.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/util/json.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/util/list.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/util/path.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/util/thread.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/util/win32_process.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok/util/yaml.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok_script.egg-info/dependency_links.txt +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok_script.egg-info/requires.txt +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/ok_script.egg-info/top_level.txt +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/setup.cfg +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/tests/__init__.py +0 -0
- {ok-script-0.0.86 → ok-script-0.0.96}/tests/test_updater.py +0 -0
|
@@ -37,6 +37,9 @@ class OK:
|
|
|
37
37
|
config['debug'] = config.get("debug", False)
|
|
38
38
|
self.debug = config['debug']
|
|
39
39
|
try:
|
|
40
|
+
if self.debug:
|
|
41
|
+
import win32api
|
|
42
|
+
win32api.SetConsoleCtrlHandler(self.console_handler, True)
|
|
40
43
|
self.config = config
|
|
41
44
|
self.init_device_manager()
|
|
42
45
|
from ok.gui.debug.Screenshot import Screenshot
|
|
@@ -59,6 +62,7 @@ class OK:
|
|
|
59
62
|
if self.config.get("use_gui"):
|
|
60
63
|
if not self.init_error:
|
|
61
64
|
self.app.show_main_window()
|
|
65
|
+
logger.debug('start app.exec()')
|
|
62
66
|
self.app.exec()
|
|
63
67
|
else:
|
|
64
68
|
self.task_executor.start()
|
|
@@ -97,7 +101,7 @@ class OK:
|
|
|
97
101
|
if template_matching is not None:
|
|
98
102
|
coco_feature_json = self.config.get('template_matching').get('coco_feature_json')
|
|
99
103
|
from ok.feature.FeatureSet import FeatureSet
|
|
100
|
-
self.feature_set = FeatureSet(coco_feature_json,
|
|
104
|
+
self.feature_set = FeatureSet(self.debug, coco_feature_json,
|
|
101
105
|
default_horizontal_variance=template_matching.get(
|
|
102
106
|
'default_horizontal_variance', 0),
|
|
103
107
|
default_vertical_variance=template_matching.get('default_vertical_variance',
|
|
@@ -110,7 +114,7 @@ class OK:
|
|
|
110
114
|
trigger_tasks=self.config.get('trigger_tasks', []),
|
|
111
115
|
scenes=self.config['scenes'],
|
|
112
116
|
feature_set=self.feature_set,
|
|
113
|
-
config_folder=self.config.get("config_folder"))
|
|
117
|
+
config_folder=self.config.get("config_folder"), debug=self.debug)
|
|
114
118
|
|
|
115
119
|
ok.gui.executor = self.task_executor
|
|
116
120
|
|
|
@@ -135,6 +139,26 @@ class OK:
|
|
|
135
139
|
while not self.exit_event.is_set():
|
|
136
140
|
time.sleep(1)
|
|
137
141
|
|
|
142
|
+
def console_handler(self, event):
|
|
143
|
+
import win32con
|
|
144
|
+
if event == win32con.CTRL_C_EVENT:
|
|
145
|
+
logger.info("CTRL+C event dump threads")
|
|
146
|
+
from ok.capture.windows.dump import dump_threads
|
|
147
|
+
dump_threads()
|
|
148
|
+
self.quit()
|
|
149
|
+
elif event == win32con.CTRL_CLOSE_EVENT:
|
|
150
|
+
logger.info("Close event quit")
|
|
151
|
+
self.quit()
|
|
152
|
+
elif event == win32con.CTRL_LOGOFF_EVENT:
|
|
153
|
+
logger.info("Logoff event quit")
|
|
154
|
+
self.quit()
|
|
155
|
+
elif event == win32con.CTRL_SHUTDOWN_EVENT:
|
|
156
|
+
logger.info("Shutdown event quit")
|
|
157
|
+
self.quit()
|
|
158
|
+
else: # Perform clean-up tasks here
|
|
159
|
+
print("Performing clean-up...")
|
|
160
|
+
return True
|
|
161
|
+
|
|
138
162
|
def quit(self):
|
|
139
163
|
logger.debug('quit app')
|
|
140
164
|
self.exit_event.set()
|
|
@@ -24,20 +24,10 @@ class BaseCaptureMethod:
|
|
|
24
24
|
|
|
25
25
|
@property
|
|
26
26
|
def width(self):
|
|
27
|
-
if self._size[0] == 0:
|
|
28
|
-
try:
|
|
29
|
-
self.get_frame()
|
|
30
|
-
except Exception as e:
|
|
31
|
-
logger.error('width get frame error', e)
|
|
32
27
|
return self._size[0]
|
|
33
28
|
|
|
34
29
|
@property
|
|
35
30
|
def height(self):
|
|
36
|
-
if self._size[1] == 0:
|
|
37
|
-
try:
|
|
38
|
-
self.get_frame()
|
|
39
|
-
except Exception as e:
|
|
40
|
-
logger.error('height get frame error', e)
|
|
41
31
|
return self._size[1]
|
|
42
32
|
|
|
43
33
|
def get_frame(self) -> np.ndarray | None:
|
|
@@ -29,7 +29,6 @@ class HwndWindow:
|
|
|
29
29
|
left_cut = 0
|
|
30
30
|
ext_left_bounds = 0 # for BitBlt
|
|
31
31
|
ext_top_bounds = 0 # for BitBlt
|
|
32
|
-
window_change_listeners = []
|
|
33
32
|
frame_aspect_ratio = 0
|
|
34
33
|
hwnd = None
|
|
35
34
|
frame_width = 0
|
|
@@ -178,10 +177,9 @@ def find_hwnd(title, exe_name, frame_width, frame_height, player_id=-1):
|
|
|
178
177
|
|
|
179
178
|
win32gui.EnumWindows(callback, None)
|
|
180
179
|
if len(results) > 0:
|
|
181
|
-
biggest =
|
|
180
|
+
biggest = None
|
|
182
181
|
for result in results:
|
|
183
|
-
|
|
184
|
-
if (biggest is None or (result[2] * result[3]) > biggest[2] * biggest[3]) and bit_blt_test_hwnd(result[0]):
|
|
182
|
+
if biggest is None or (result[2] * result[3]) > biggest[2] * biggest[3]:
|
|
185
183
|
biggest = result
|
|
186
184
|
x_offset = 0
|
|
187
185
|
y_offset = 0
|
|
@@ -67,7 +67,7 @@ class DeviceManager:
|
|
|
67
67
|
logger.debug(f'adb_connect already connected {addr}')
|
|
68
68
|
return device
|
|
69
69
|
if try_connect:
|
|
70
|
-
self.adb.connect(addr, timeout=
|
|
70
|
+
self.adb.connect(addr, timeout=3)
|
|
71
71
|
logger.debug(f'adb_connect {addr}')
|
|
72
72
|
return self.adb_connect(addr, try_connect=False)
|
|
73
73
|
except AdbTimeout as e:
|
|
@@ -116,8 +116,8 @@ class DeviceManager:
|
|
|
116
116
|
if imei is not None:
|
|
117
117
|
preferred = self.get_preferred_device()
|
|
118
118
|
if current and preferred is not None and preferred['imei'] != imei:
|
|
119
|
-
logger.debug(f"refresh current only skip others {preferred['imei']} != imei")
|
|
120
|
-
|
|
119
|
+
logger.debug(f"refresh current only skip others {preferred['imei']} != {imei}")
|
|
120
|
+
continue
|
|
121
121
|
found = False
|
|
122
122
|
for device in self.device_dict.values():
|
|
123
123
|
if device.get('adb_imei') == imei:
|
|
@@ -263,12 +263,12 @@ class DeviceManager:
|
|
|
263
263
|
self.use_windows_capture()
|
|
264
264
|
if not isinstance(self.interaction, Win32Interaction):
|
|
265
265
|
self.interaction = Win32Interaction(self.capture_method)
|
|
266
|
-
preferred['connected'] = self.capture_method.connected()
|
|
266
|
+
preferred['connected'] = self.capture_method is not None and self.capture_method.connected()
|
|
267
267
|
else:
|
|
268
268
|
width, height = self.get_resolution()
|
|
269
269
|
if self.config.get('capture') == "windows":
|
|
270
270
|
self.ensure_hwnd(None, preferred.get('full_path'), width, height, preferred['player_id'])
|
|
271
|
-
self.use_windows_capture({'can_bit_blt': True}, require_bg=True, use_bit_blt_only=
|
|
271
|
+
self.use_windows_capture({'can_bit_blt': True}, require_bg=True, use_bit_blt_only=False)
|
|
272
272
|
else:
|
|
273
273
|
if not isinstance(self.capture_method, ADBCaptureMethod):
|
|
274
274
|
logger.debug(f'use adb capture')
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from ok.logging.Logger import get_logger
|
|
2
|
+
|
|
3
|
+
logger = get_logger(__name__)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def update_capture_method(config, capture_method, hwnd, require_bg=False, use_bit_blt_only=False):
|
|
7
|
+
try:
|
|
8
|
+
if config.get('can_bit_blt'):
|
|
9
|
+
logger.debug(
|
|
10
|
+
f"try BitBlt method {config} {hwnd} current_type:{type(capture_method)}")
|
|
11
|
+
from ok.capture.windows.BitBltCaptureMethod import BitBltCaptureMethod
|
|
12
|
+
target_method = BitBltCaptureMethod
|
|
13
|
+
capture_method = get_capture(capture_method, target_method, hwnd)
|
|
14
|
+
if capture_method.test_is_not_pure_color():
|
|
15
|
+
return capture_method
|
|
16
|
+
else:
|
|
17
|
+
logger.info("test_is_not_pure_color failed, can't use BitBlt")
|
|
18
|
+
if use_bit_blt_only:
|
|
19
|
+
return None
|
|
20
|
+
from ok.capture.windows.WindowsGraphicsCaptureMethod import windows_graphics_available
|
|
21
|
+
if windows_graphics_available():
|
|
22
|
+
from ok.capture.windows.WindowsGraphicsCaptureMethod import WindowsGraphicsCaptureMethod
|
|
23
|
+
target_method = WindowsGraphicsCaptureMethod
|
|
24
|
+
capture_method = get_capture(capture_method, target_method, hwnd)
|
|
25
|
+
return capture_method
|
|
26
|
+
|
|
27
|
+
if not require_bg:
|
|
28
|
+
from ok.capture.windows.DesktopDuplicationCaptureMethod import DesktopDuplicationCaptureMethod
|
|
29
|
+
target_method = DesktopDuplicationCaptureMethod
|
|
30
|
+
capture_method = get_capture(capture_method, target_method, hwnd)
|
|
31
|
+
return capture_method
|
|
32
|
+
except Exception as e:
|
|
33
|
+
logger.error(f'update_capture_method exception, return None: ', e)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def get_capture(capture_method, target_method, hwnd):
|
|
37
|
+
if not isinstance(capture_method, target_method):
|
|
38
|
+
if capture_method is not None:
|
|
39
|
+
capture_method.close()
|
|
40
|
+
capture_method = target_method(hwnd)
|
|
41
|
+
capture_method.hwnd_window = hwnd
|
|
42
|
+
return capture_method
|
|
@@ -10,7 +10,7 @@ from typing_extensions import override
|
|
|
10
10
|
|
|
11
11
|
from ok.capture.windows.BaseWindowsCaptureMethod import BaseWindowsCaptureMethod
|
|
12
12
|
from ok.capture.windows.utils import try_delete_dc, BGRA_CHANNEL_COUNT
|
|
13
|
-
from ok.
|
|
13
|
+
from ok.color.Color import is_close_to_pure_color
|
|
14
14
|
from ok.logging.Logger import get_logger
|
|
15
15
|
|
|
16
16
|
# This is an undocumented nFlag value for PrintWindow
|
|
@@ -60,12 +60,17 @@ class BitBltCaptureMethod(BaseWindowsCaptureMethod):
|
|
|
60
60
|
return False
|
|
61
61
|
return True
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
def test_is_not_pure_color(self):
|
|
64
|
+
frame = self.do_get_frame()
|
|
65
|
+
if frame is None:
|
|
66
|
+
logger.error(f'Failed to test_is_not_pure_color frame is None {self.hwnd_window}')
|
|
67
|
+
return False
|
|
68
|
+
else:
|
|
69
|
+
if is_close_to_pure_color(frame):
|
|
70
|
+
logger.error(f'Failed to test_is_not_pure_color failed {self.hwnd_window}')
|
|
71
|
+
return False
|
|
72
|
+
else:
|
|
73
|
+
return True
|
|
69
74
|
|
|
70
75
|
|
|
71
76
|
def bit_blt_capture_frame(hwnd, border, title_height, width, height, ext_left_bounds, ext_top_bounds,
|
|
@@ -23,7 +23,7 @@ logger = get_logger(__name__)
|
|
|
23
23
|
class WindowsGraphicsCaptureMethod(BaseWindowsCaptureMethod):
|
|
24
24
|
name = "Windows Graphics Capture"
|
|
25
25
|
description = "fast, most compatible, capped at 60fps"
|
|
26
|
-
|
|
26
|
+
last_dx_frame = None
|
|
27
27
|
last_frame_time = 0
|
|
28
28
|
frame_pool = None
|
|
29
29
|
item = None
|
|
@@ -38,67 +38,78 @@ class WindowsGraphicsCaptureMethod(BaseWindowsCaptureMethod):
|
|
|
38
38
|
|
|
39
39
|
def frame_arrived_callback(self, x, y):
|
|
40
40
|
try:
|
|
41
|
-
|
|
41
|
+
self.last_frame_time = time.time()
|
|
42
|
+
next_frame = self.frame_pool.TryGetNextFrame()
|
|
43
|
+
if next_frame is not None:
|
|
44
|
+
self.last_dx_frame = next_frame
|
|
45
|
+
else:
|
|
46
|
+
logger.warning('frame_arrived_callback TryGetNextFrame returned None')
|
|
42
47
|
except Exception as e:
|
|
43
48
|
logger.error(f"TryGetNextFrame error {e}")
|
|
44
49
|
self.close()
|
|
45
50
|
return
|
|
51
|
+
|
|
52
|
+
def convert_dx_frame(self):
|
|
53
|
+
frame = self.last_dx_frame
|
|
54
|
+
self.last_dx_frame = None
|
|
46
55
|
if not frame:
|
|
56
|
+
logger.warning('convert_dx_frame self.last_dx_frame is none')
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
need_reset_framepool = False
|
|
60
|
+
if frame.ContentSize.Width != self.last_size.Width or frame.ContentSize.Height != self.last_size.Height:
|
|
61
|
+
need_reset_framepool = True
|
|
62
|
+
self.last_size = frame.ContentSize
|
|
63
|
+
|
|
64
|
+
if need_reset_framepool:
|
|
65
|
+
logger.info('need_reset_framepool')
|
|
66
|
+
self.reset_framepool(frame.ContentSize)
|
|
47
67
|
return
|
|
48
|
-
|
|
49
|
-
img = None
|
|
50
|
-
with frame:
|
|
51
|
-
need_reset_framepool = False
|
|
52
|
-
need_reset_device = False
|
|
53
|
-
if frame.ContentSize.Width != self.last_size.Width or frame.ContentSize.Height != self.last_size.Height:
|
|
54
|
-
need_reset_framepool = True
|
|
55
|
-
self.last_size = frame.ContentSize
|
|
68
|
+
need_reset_device = False
|
|
56
69
|
|
|
57
|
-
|
|
58
|
-
self.reset_framepool(frame.ContentSize)
|
|
59
|
-
return
|
|
60
|
-
tex = None
|
|
70
|
+
tex = None
|
|
61
71
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
72
|
+
cputex = None
|
|
73
|
+
try:
|
|
74
|
+
from ok.rotypes.Windows.Graphics.DirectX.Direct3D11 import IDirect3DDxgiInterfaceAccess
|
|
75
|
+
from ok.rotypes.roapi import GetActivationFactory
|
|
76
|
+
tex = frame.Surface.astype(IDirect3DDxgiInterfaceAccess).GetInterface(
|
|
77
|
+
d3d11.ID3D11Texture2D.GUID).astype(d3d11.ID3D11Texture2D)
|
|
78
|
+
desc = tex.GetDesc()
|
|
79
|
+
desc2 = d3d11.D3D11_TEXTURE2D_DESC()
|
|
80
|
+
desc2.Width = desc.Width
|
|
81
|
+
desc2.Height = desc.Height
|
|
82
|
+
desc2.MipLevels = desc.MipLevels
|
|
83
|
+
desc2.ArraySize = desc.ArraySize
|
|
84
|
+
desc2.Format = desc.Format
|
|
85
|
+
desc2.SampleDesc = desc.SampleDesc
|
|
86
|
+
desc2.Usage = d3d11.D3D11_USAGE_STAGING
|
|
87
|
+
desc2.CPUAccessFlags = d3d11.D3D11_CPU_ACCESS_READ
|
|
88
|
+
desc2.BindFlags = 0
|
|
89
|
+
desc2.MiscFlags = 0
|
|
90
|
+
cputex = self.dxdevice.CreateTexture2D(ctypes.byref(desc2), None)
|
|
91
|
+
self.immediatedc.CopyResource(cputex, tex)
|
|
92
|
+
mapinfo = self.immediatedc.Map(cputex, 0, d3d11.D3D11_MAP_READ, 0)
|
|
93
|
+
img = np.ctypeslib.as_array(ctypes.cast(mapinfo.pData, PBYTE),
|
|
94
|
+
(desc.Height, mapinfo.RowPitch // 4, 4))[
|
|
95
|
+
:, :desc.Width].copy()
|
|
96
|
+
self.immediatedc.Unmap(cputex, 0)
|
|
97
|
+
return img
|
|
98
|
+
except OSError as e:
|
|
99
|
+
if e.winerror == d3d11.DXGI_ERROR_DEVICE_REMOVED or e.winerror == d3d11.DXGI_ERROR_DEVICE_RESET:
|
|
100
|
+
need_reset_framepool = True
|
|
101
|
+
need_reset_device = True
|
|
102
|
+
logger.error('convert_dx_frame win error', e)
|
|
103
|
+
else:
|
|
104
|
+
raise e
|
|
105
|
+
finally:
|
|
106
|
+
if tex is not None:
|
|
107
|
+
tex.Release()
|
|
108
|
+
if cputex is not None:
|
|
109
|
+
cputex.Release()
|
|
110
|
+
if need_reset_framepool:
|
|
111
|
+
self.reset_framepool(frame.ContentSize, need_reset_device)
|
|
112
|
+
return self.get_frame()
|
|
102
113
|
|
|
103
114
|
@property
|
|
104
115
|
def hwnd_window(self):
|
|
@@ -139,7 +150,7 @@ class WindowsGraphicsCaptureMethod(BaseWindowsCaptureMethod):
|
|
|
139
150
|
self.evtoken = item.add_Closed(delegate)
|
|
140
151
|
self.frame_pool = Direct3D11CaptureFramePool.CreateFreeThreaded(self.rtdevice,
|
|
141
152
|
DirectXPixelFormat.B8G8R8A8UIntNormalized,
|
|
142
|
-
|
|
153
|
+
2, item.Size)
|
|
143
154
|
self.session = self.frame_pool.CreateCaptureSession(item)
|
|
144
155
|
pool = self.frame_pool
|
|
145
156
|
pool.add_FrameArrived(
|
|
@@ -193,10 +204,10 @@ class WindowsGraphicsCaptureMethod(BaseWindowsCaptureMethod):
|
|
|
193
204
|
@override
|
|
194
205
|
def do_get_frame(self):
|
|
195
206
|
if self.start_or_stop():
|
|
196
|
-
frame = self.
|
|
197
|
-
|
|
207
|
+
frame = self.convert_dx_frame()
|
|
208
|
+
if frame is None:
|
|
209
|
+
return
|
|
198
210
|
latency = time.time() - self.last_frame_time
|
|
199
|
-
self.last_frame_time = time.time()
|
|
200
211
|
|
|
201
212
|
frame = self.crop_image(frame)
|
|
202
213
|
|
|
@@ -205,18 +216,20 @@ class WindowsGraphicsCaptureMethod(BaseWindowsCaptureMethod):
|
|
|
205
216
|
if new_width <= 0 or new_width <= 0:
|
|
206
217
|
logger.warning(f"get_frame size <=0 {new_width}x{new_height}")
|
|
207
218
|
frame = None
|
|
208
|
-
if latency >
|
|
219
|
+
if latency > 2:
|
|
209
220
|
logger.warning(f"latency too large return None frame: {latency}")
|
|
210
221
|
return None
|
|
211
222
|
else:
|
|
223
|
+
# logger.debug(f'frame latency: {latency}')
|
|
212
224
|
return frame
|
|
213
225
|
|
|
214
226
|
def reset_framepool(self, size, reset_device=False):
|
|
227
|
+
logger.info(f'reset_framepool')
|
|
215
228
|
from ok.rotypes.Windows.Graphics.DirectX import DirectXPixelFormat
|
|
216
229
|
if reset_device:
|
|
217
230
|
self.create_device()
|
|
218
231
|
self.frame_pool.Recreate(self.rtdevice,
|
|
219
|
-
DirectXPixelFormat.B8G8R8A8UIntNormalized,
|
|
232
|
+
DirectXPixelFormat.B8G8R8A8UIntNormalized, 2, size)
|
|
220
233
|
|
|
221
234
|
def crop_image(self, frame):
|
|
222
235
|
if frame is not None:
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import signal
|
|
3
|
+
import sys
|
|
4
|
+
import threading
|
|
5
|
+
import traceback
|
|
6
|
+
|
|
7
|
+
from ok.util.path import get_path_relative_to_exe
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_thread_name(thread_id):
|
|
11
|
+
for thread in threading.enumerate():
|
|
12
|
+
if thread.ident == thread_id:
|
|
13
|
+
return thread.name
|
|
14
|
+
return "" # Return empty string if the thread name is not found
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def dump_threads():
|
|
18
|
+
# Get the stack trace for each thread
|
|
19
|
+
thread_dumps = []
|
|
20
|
+
for thread_id, frame in sys._current_frames().items():
|
|
21
|
+
thread_name = get_thread_name(thread_id)
|
|
22
|
+
thread_dump = f"Stack for thread {thread_id} (Name: {thread_name}):\n"
|
|
23
|
+
thread_dump += "".join(traceback.format_stack(frame))
|
|
24
|
+
thread_dumps.append(thread_dump)
|
|
25
|
+
|
|
26
|
+
# Write the thread dumps to a file
|
|
27
|
+
output_file = get_path_relative_to_exe(os.path.join('logs', "thread_dumps.txt"))
|
|
28
|
+
print(f'Dumping threads to {output_file}')
|
|
29
|
+
|
|
30
|
+
with open(output_file, "w", encoding='utf-8') as f:
|
|
31
|
+
f.write("\n\n".join(thread_dumps))
|
|
32
|
+
|
|
33
|
+
# subprocess.Popen(r'explorer /select,"{}"'.format(output_file))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def kill_dump():
|
|
37
|
+
print("Killing dump")
|
|
38
|
+
dump_threads()
|
|
39
|
+
sys.exit(0)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def console_handler(event):
|
|
43
|
+
import win32con
|
|
44
|
+
if event == win32con.CTRL_C_EVENT:
|
|
45
|
+
print("CTRL+C event")
|
|
46
|
+
dump_threads()
|
|
47
|
+
sys.exit(0)
|
|
48
|
+
elif event == win32con.CTRL_CLOSE_EVENT:
|
|
49
|
+
print("Close event")
|
|
50
|
+
elif event == win32con.CTRL_LOGOFF_EVENT:
|
|
51
|
+
print("Logoff event")
|
|
52
|
+
elif event == win32con.CTRL_SHUTDOWN_EVENT:
|
|
53
|
+
print("Shutdown event")
|
|
54
|
+
# Perform clean-up tasks here
|
|
55
|
+
print("Performing clean-up...")
|
|
56
|
+
# sys.exit(0) # Exit the program
|
|
57
|
+
return True
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
if __name__ == '__main__':
|
|
61
|
+
|
|
62
|
+
import psutil
|
|
63
|
+
import faulthandler
|
|
64
|
+
|
|
65
|
+
# Enable the fault handler
|
|
66
|
+
faulthandler.enable()
|
|
67
|
+
|
|
68
|
+
# Iterate over all running processes
|
|
69
|
+
for proc in psutil.process_iter(['pid', 'name', 'status', 'exe']):
|
|
70
|
+
# Check if the process is unresponsive
|
|
71
|
+
# print(proc.info['exe'])
|
|
72
|
+
if proc.info['name'] == 'python.exe':
|
|
73
|
+
# Dump the traceback
|
|
74
|
+
print(f'dump {proc.pid} {proc.cmdline()} {proc.info["pid"] == os.getpid()}')
|
|
75
|
+
# faulthandler.dump_traceback(file=sys.stderr, all_threads=True)
|
|
76
|
+
# Kill the process
|
|
77
|
+
# os.kill(proc.info['pid'], signal.SIGTERM)
|
|
78
|
+
|
|
79
|
+
# faulthandler.dump_traceback(file=sys.stderr, all_threads=True)
|
|
80
|
+
# Kill the process
|
|
81
|
+
os.kill(16916, signal.SIGTERM)
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from collections import defaultdict
|
|
3
|
+
|
|
1
4
|
import cv2
|
|
2
5
|
import numpy as np
|
|
3
6
|
|
|
@@ -16,6 +19,29 @@ white_color = {
|
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
|
|
22
|
+
def is_close_to_pure_color(image, max_colors=5000, percent=0.97):
|
|
23
|
+
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
|
24
|
+
|
|
25
|
+
# Step 2: Initialize a dictionary to count color frequencies
|
|
26
|
+
color_counts = defaultdict(int)
|
|
27
|
+
# Step 3: Go through each pixel and count
|
|
28
|
+
for row in image:
|
|
29
|
+
for pixel in row:
|
|
30
|
+
# Convert the pixel tuple to a hashable type for counting
|
|
31
|
+
color = tuple(pixel)
|
|
32
|
+
color_counts[color] += 1
|
|
33
|
+
if len(color_counts) > max_colors:
|
|
34
|
+
return False
|
|
35
|
+
|
|
36
|
+
# Step 4: Find the most dominant color and its percentage
|
|
37
|
+
dominant_color = max(color_counts, key=color_counts.get)
|
|
38
|
+
dominant_count = color_counts[dominant_color]
|
|
39
|
+
total_pixels = image.shape[0] * image.shape[1]
|
|
40
|
+
percentage = (dominant_count / total_pixels)
|
|
41
|
+
|
|
42
|
+
return percentage > percent
|
|
43
|
+
|
|
44
|
+
|
|
19
45
|
def calculate_colorfulness(image, box=None):
|
|
20
46
|
if box is not None:
|
|
21
47
|
image = image[box.y:box.y + box.height, box.x:box.x + box.width, :3]
|
|
@@ -146,3 +172,11 @@ def calculate_color_percentage(image, color_ranges, box=None):
|
|
|
146
172
|
total_pixels = image.size / 3 # Divide by 3 for an RGB image
|
|
147
173
|
percentage = target_pixels / total_pixels
|
|
148
174
|
return percentage
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
if __name__ == '__main__':
|
|
178
|
+
file = 'C:\\Users\\ok\Downloads\\ok-baijing (2)\\screenshots\\1.png'
|
|
179
|
+
exists = os.path.exists(file)
|
|
180
|
+
image = cv2.imread(file
|
|
181
|
+
)
|
|
182
|
+
print(is_close_to_pure_color(image))
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
import cv2
|
|
6
|
+
import numpy as np
|
|
7
|
+
from PIL import Image
|
|
8
|
+
from PIL.PngImagePlugin import PngInfo
|
|
9
|
+
|
|
10
|
+
from ok.feature.FeatureSet import read_from_json
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def compress_coco(coco_json, canny_upper, canny_lower) -> None:
|
|
14
|
+
feature_dict, *_ = read_from_json(coco_json, canny_lower=canny_lower, canny_upper=canny_upper)
|
|
15
|
+
with open(coco_json, 'r') as file:
|
|
16
|
+
image_dict = {}
|
|
17
|
+
data = json.load(file)
|
|
18
|
+
coco_folder = os.path.dirname(coco_json)
|
|
19
|
+
image_map = {image['id']: image['file_name'] for image in data['images']}
|
|
20
|
+
category_map = {category['id']: category['name'] for category in data['categories']}
|
|
21
|
+
|
|
22
|
+
for annotation in data['annotations']:
|
|
23
|
+
image_id = annotation['image_id']
|
|
24
|
+
category_id = annotation['category_id']
|
|
25
|
+
|
|
26
|
+
feature = feature_dict.get(category_map[category_id])
|
|
27
|
+
if feature:
|
|
28
|
+
# Load and scale the image
|
|
29
|
+
image_path = str(os.path.join(coco_folder, image_map[image_id]))
|
|
30
|
+
image_features = image_dict.get(image_path, [])
|
|
31
|
+
image_features.append(feature)
|
|
32
|
+
image_dict[image_path] = image_features
|
|
33
|
+
|
|
34
|
+
# Loop through the image_dict and write all the image_feature associated with it in a new PNG
|
|
35
|
+
for image_path, features in image_dict.items():
|
|
36
|
+
background = None
|
|
37
|
+
for feature in features:
|
|
38
|
+
# Create a white background
|
|
39
|
+
if background is None:
|
|
40
|
+
original_image = cv2.imread(image_path)
|
|
41
|
+
background = np.full_like(original_image,
|
|
42
|
+
255) # Create white background with the same shape as original_image
|
|
43
|
+
|
|
44
|
+
# Paste the feature onto the background
|
|
45
|
+
x, y = feature.x, feature.y
|
|
46
|
+
h, w = feature.mat.shape[:2]
|
|
47
|
+
background[y:y + h, x:x + w] = feature.mat
|
|
48
|
+
|
|
49
|
+
# Add metadata
|
|
50
|
+
metadata = {
|
|
51
|
+
"ok_compressed": "1"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Save the image with metadata
|
|
55
|
+
save_image_with_metadata(background, image_path)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def save_image_with_metadata(image, image_path):
|
|
59
|
+
# Convert OpenCV image (numpy array) to PIL Image
|
|
60
|
+
pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
|
|
61
|
+
|
|
62
|
+
metadata = PngInfo()
|
|
63
|
+
|
|
64
|
+
# Add metadata
|
|
65
|
+
metadata.add_text('ok_compressed', '1')
|
|
66
|
+
metadata.add_text("Author", "ok_compress")
|
|
67
|
+
metadata.add_text("Description", "This is a sample image")
|
|
68
|
+
|
|
69
|
+
# Save the image with metadata
|
|
70
|
+
pil_image.save(image_path, 'PNG', optimize=True, pnginfo=metadata)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
if __name__ == '__main__':
|
|
74
|
+
json_file = sys.argv[1]
|
|
75
|
+
compress_coco(json_file)
|
|
@@ -2,7 +2,7 @@ import numpy as np
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class Feature:
|
|
5
|
-
def __init__(self, mat: np.ndarray, x: int, y: int
|
|
5
|
+
def __init__(self, mat: np.ndarray, x: int, y: int) -> None:
|
|
6
6
|
"""
|
|
7
7
|
Initialize a Feature with an image (Mat) and its bounding box coordinates.
|
|
8
8
|
|
|
@@ -14,10 +14,16 @@ class Feature:
|
|
|
14
14
|
height (int): The height of the bounding box.
|
|
15
15
|
"""
|
|
16
16
|
self.mat = mat
|
|
17
|
-
self.x = x
|
|
18
|
-
self.y = y
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
self.x = round(x)
|
|
18
|
+
self.y = round(y)
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def width(self):
|
|
22
|
+
return self.mat.shape[1]
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def height(self):
|
|
26
|
+
return self.mat.shape[0]
|
|
21
27
|
|
|
22
28
|
def __str__(self) -> str:
|
|
23
29
|
return f'self.x: {self.x}, self.y: {self.y}, width: {self.width}, height: {self.height}'
|