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.
Files changed (185) hide show
  1. {ok-script-0.0.86 → ok-script-0.0.96}/PKG-INFO +1 -1
  2. {ok-script-0.0.86 → ok-script-0.0.96}/ok/OK.py +26 -2
  3. {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/BaseCaptureMethod.py +0 -10
  4. {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/HwndWindow.py +2 -4
  5. {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/adb/DeviceManager.py +5 -5
  6. ok-script-0.0.96/ok/capture/adb/WindowsCaptureFactory.py +42 -0
  7. {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/windows/BitBltCaptureMethod.py +12 -7
  8. {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/windows/WindowsGraphicsCaptureMethod.py +73 -60
  9. ok-script-0.0.96/ok/capture/windows/dump.py +81 -0
  10. {ok-script-0.0.86 → ok-script-0.0.96}/ok/color/Color.py +34 -0
  11. ok-script-0.0.96/ok/feature/CompressCoco.py +75 -0
  12. {ok-script-0.0.86 → ok-script-0.0.96}/ok/feature/Feature.py +11 -5
  13. {ok-script-0.0.86 → ok-script-0.0.96}/ok/feature/FeatureSet.py +132 -75
  14. {ok-script-0.0.86 → ok-script-0.0.96}/ok/firebase/Analytics.py +2 -2
  15. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/App.py +10 -9
  16. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/MainWindow.py +4 -16
  17. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/StartController.py +2 -2
  18. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/debug/DebugTab.py +20 -18
  19. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/debug/LogWindow.py +33 -31
  20. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/resources.py +433 -432
  21. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/start/StartCard.py +9 -0
  22. ok-script-0.0.96/ok/gui/tasks/OneTimeTaskTab.py +20 -0
  23. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/TaskOpButton.py +2 -8
  24. ok-script-0.0.86/ok/gui/tasks/OneTimeTaskTab.py → ok-script-0.0.96/ok/gui/tasks/TaskTab.py +5 -7
  25. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/TriggerTaskTab.py +5 -2
  26. {ok-script-0.0.86 → ok-script-0.0.96}/ok/interaction/BaseInteraction.py +6 -0
  27. {ok-script-0.0.86 → ok-script-0.0.96}/ok/interaction/Win32Interaction.py +18 -0
  28. {ok-script-0.0.86 → ok-script-0.0.96}/ok/ocr/OCR.py +3 -3
  29. {ok-script-0.0.86 → ok-script-0.0.96}/ok/task/BaseTask.py +12 -15
  30. {ok-script-0.0.86 → ok-script-0.0.96}/ok/task/ExecutorOperation.py +20 -10
  31. {ok-script-0.0.86 → ok-script-0.0.96}/ok/task/TaskExecutor.py +44 -45
  32. {ok-script-0.0.86 → ok-script-0.0.96}/ok/task/TriggerTask.py +0 -1
  33. {ok-script-0.0.86 → ok-script-0.0.96}/ok/update/GithubMultiDownloader.py +48 -11
  34. {ok-script-0.0.86 → ok-script-0.0.96}/ok/update/Updater.py +49 -28
  35. {ok-script-0.0.86 → ok-script-0.0.96}/ok/util/Handler.py +1 -0
  36. {ok-script-0.0.86 → ok-script-0.0.96}/ok_script.egg-info/PKG-INFO +1 -1
  37. {ok-script-0.0.86 → ok-script-0.0.96}/ok_script.egg-info/SOURCES.txt +2 -0
  38. {ok-script-0.0.86 → ok-script-0.0.96}/setup.py +1 -1
  39. ok-script-0.0.86/ok/capture/adb/WindowsCaptureFactory.py +0 -41
  40. ok-script-0.0.86/ok/capture/windows/dump.py +0 -31
  41. {ok-script-0.0.86 → ok-script-0.0.96}/README.md +0 -0
  42. {ok-script-0.0.86 → ok-script-0.0.96}/ok/__init__.py +0 -0
  43. {ok-script-0.0.86 → ok-script-0.0.96}/ok/alas/__init__.py +0 -0
  44. {ok-script-0.0.86 → ok-script-0.0.96}/ok/alas/emulator_base.py +0 -0
  45. {ok-script-0.0.86 → ok-script-0.0.96}/ok/alas/emulator_windows.py +0 -0
  46. {ok-script-0.0.86 → ok-script-0.0.96}/ok/alas/platform_base.py +0 -0
  47. {ok-script-0.0.86 → ok-script-0.0.96}/ok/alas/platform_windows.py +0 -0
  48. {ok-script-0.0.86 → ok-script-0.0.96}/ok/alas/utils.py +0 -0
  49. {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/__init__.py +0 -0
  50. {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/adb/ADBCaptureMethod.py +0 -0
  51. {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/adb/__init__.py +0 -0
  52. {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/adb/bin/__init__.py +0 -0
  53. {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/image/ImageCaptureMethod.py +0 -0
  54. {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/image/__init__.py +0 -0
  55. {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/windows/BaseWindowsCaptureMethod.py +0 -0
  56. {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/windows/DesktopDuplicationCaptureMethod.py +0 -0
  57. {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/windows/__init__.py +0 -0
  58. {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/windows/d3d11.py +0 -0
  59. {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/windows/utils.py +0 -0
  60. {ok-script-0.0.86 → ok-script-0.0.96}/ok/capture/windows/window.py +0 -0
  61. {ok-script-0.0.86 → ok-script-0.0.96}/ok/color/__init__.py +0 -0
  62. {ok-script-0.0.86 → ok-script-0.0.96}/ok/config/Config.py +0 -0
  63. {ok-script-0.0.86 → ok-script-0.0.96}/ok/config/InfoDict.py +0 -0
  64. {ok-script-0.0.86 → ok-script-0.0.96}/ok/config/__init__.py +0 -0
  65. {ok-script-0.0.86 → ok-script-0.0.96}/ok/feature/Box.py +0 -0
  66. {ok-script-0.0.86 → ok-script-0.0.96}/ok/feature/FindFeature.py +0 -0
  67. {ok-script-0.0.86 → ok-script-0.0.96}/ok/feature/__init__.py +0 -0
  68. {ok-script-0.0.86 → ok-script-0.0.96}/ok/firebase/__init__.py +0 -0
  69. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/Communicate.py +0 -0
  70. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/MessageWindow.py +0 -0
  71. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/TabTitles.py +0 -0
  72. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/__init__.py +0 -0
  73. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/about/AboutTab.py +0 -0
  74. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/about/HttpTextEdit.py +0 -0
  75. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/about/VersionCard.py +0 -0
  76. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/about/__init__.py +0 -0
  77. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/common/OKIcon.py +0 -0
  78. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/common/__init__.py +0 -0
  79. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/common/config.py +0 -0
  80. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/common/style_sheet.py +0 -0
  81. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/debug/AspectRatioWidget.py +0 -0
  82. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/debug/FrameWidget.py +0 -0
  83. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/debug/InfoWidget.py +0 -0
  84. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/debug/Screenshot.py +0 -0
  85. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/debug/__init__.py +0 -0
  86. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/i18n/__init__.py +0 -0
  87. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/i18n/path.py +0 -0
  88. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/i18n/update.py +0 -0
  89. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/icon/__init__.py +0 -0
  90. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/icon/icon.py +0 -0
  91. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/overlay/OverlayWindow.py +0 -0
  92. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/overlay/__init__.py +0 -0
  93. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/qss/__init__.py +0 -0
  94. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/settings/SettingTab.py +0 -0
  95. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/settings/__init__.py +0 -0
  96. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/start/SelectCaptureListView.py +0 -0
  97. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/start/StartTab.py +0 -0
  98. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/start/__init__.py +0 -0
  99. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/ConfigItemFactory.py +0 -0
  100. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/ConfigLabelAndWidget.py +0 -0
  101. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/LabelAndDoubleSpinBox.py +0 -0
  102. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/LabelAndDropDown.py +0 -0
  103. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/LabelAndLineEdit.py +0 -0
  104. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/LabelAndList.py +0 -0
  105. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/LabelAndSpinBox.py +0 -0
  106. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/LabelAndSwitchButton.py +0 -0
  107. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/LabelAndWidget.py +0 -0
  108. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/ModifyListDialog.py +0 -0
  109. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/ModifyListItem.py +0 -0
  110. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/StartButton.py +0 -0
  111. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/TaskCard.py +0 -0
  112. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/TooltipTableWidget.py +0 -0
  113. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/tasks/__init__.py +0 -0
  114. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/util/Alert.py +0 -0
  115. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/util/InitWorker.py +0 -0
  116. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/util/__init__.py +0 -0
  117. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/Card.py +0 -0
  118. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/EmptyCard.py +0 -0
  119. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/FramelessDialog.py +0 -0
  120. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/ListTableWidgetItem.py +0 -0
  121. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/NumericTableWidgetItem.py +0 -0
  122. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/StartLoadingDialog.py +0 -0
  123. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/StatusBar.py +0 -0
  124. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/Tab.py +0 -0
  125. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/TabWidget.py +0 -0
  126. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/UpdateConfigWidgetItem.py +0 -0
  127. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/YesNonWidgetItem.py +0 -0
  128. {ok-script-0.0.86 → ok-script-0.0.96}/ok/gui/widget/__init__.py +0 -0
  129. {ok-script-0.0.86 → ok-script-0.0.96}/ok/interaction/ADBInteraction.py +0 -0
  130. {ok-script-0.0.86 → ok-script-0.0.96}/ok/interaction/DoNothingInteraction.py +0 -0
  131. {ok-script-0.0.86 → ok-script-0.0.96}/ok/interaction/__init__.py +0 -0
  132. {ok-script-0.0.86 → ok-script-0.0.96}/ok/logging/Logger.py +0 -0
  133. {ok-script-0.0.86 → ok-script-0.0.96}/ok/logging/__init__.py +0 -0
  134. {ok-script-0.0.86 → ok-script-0.0.96}/ok/ocr/__init__.py +0 -0
  135. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Foundation/Collections.py +0 -0
  136. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Foundation/__init__.py +0 -0
  137. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Globalization/__init__.py +0 -0
  138. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Graphics/Capture/__init__.py +0 -0
  139. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Graphics/DirectX/Direct3D11/__init__.py +0 -0
  140. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Graphics/DirectX/__init__.py +0 -0
  141. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Graphics/Imaging/__init__.py +0 -0
  142. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Graphics/__init__.py +0 -0
  143. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Media/Ocr/__init__.py +0 -0
  144. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Media/__init__.py +0 -0
  145. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Security/Cryptography/__init__.py +0 -0
  146. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Security/__init__.py +0 -0
  147. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Storage/Streams/__init__.py +0 -0
  148. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/Storage/__init__.py +0 -0
  149. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/Windows/__init__.py +0 -0
  150. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/__init__.py +0 -0
  151. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/delegate.py +0 -0
  152. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/export.py +0 -0
  153. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/idldsl.py +0 -0
  154. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/inspectable.py +0 -0
  155. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/roapi.py +0 -0
  156. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/types.py +0 -0
  157. {ok-script-0.0.86 → ok-script-0.0.96}/ok/rotypes/winstring.py +0 -0
  158. {ok-script-0.0.86 → ok-script-0.0.96}/ok/save/BlackBarProcessor.py +0 -0
  159. {ok-script-0.0.86 → ok-script-0.0.96}/ok/save/PostProcessor.py +0 -0
  160. {ok-script-0.0.86 → ok-script-0.0.96}/ok/save/SaveByInterval.py +0 -0
  161. {ok-script-0.0.86 → ok-script-0.0.96}/ok/save/SaveByKeyPress.py +0 -0
  162. {ok-script-0.0.86 → ok-script-0.0.96}/ok/save/SaveMethodBase.py +0 -0
  163. {ok-script-0.0.86 → ok-script-0.0.96}/ok/save/__init__.py +0 -0
  164. {ok-script-0.0.86 → ok-script-0.0.96}/ok/scene/FeatureScene.py +0 -0
  165. {ok-script-0.0.86 → ok-script-0.0.96}/ok/scene/Scene.py +0 -0
  166. {ok-script-0.0.86 → ok-script-0.0.96}/ok/scene/__init__.py +0 -0
  167. {ok-script-0.0.86 → ok-script-0.0.96}/ok/stats/StreamStats.py +0 -0
  168. {ok-script-0.0.86 → ok-script-0.0.96}/ok/stats/__init__.py +0 -0
  169. {ok-script-0.0.86 → ok-script-0.0.96}/ok/task/__init__.py +0 -0
  170. {ok-script-0.0.86 → ok-script-0.0.96}/ok/update/__init__.py +0 -0
  171. {ok-script-0.0.86 → ok-script-0.0.96}/ok/update/gen_md5.py +0 -0
  172. {ok-script-0.0.86 → ok-script-0.0.96}/ok/util/__init__.py +0 -0
  173. {ok-script-0.0.86 → ok-script-0.0.96}/ok/util/exit_event.py +0 -0
  174. {ok-script-0.0.86 → ok-script-0.0.96}/ok/util/json.py +0 -0
  175. {ok-script-0.0.86 → ok-script-0.0.96}/ok/util/list.py +0 -0
  176. {ok-script-0.0.86 → ok-script-0.0.96}/ok/util/path.py +0 -0
  177. {ok-script-0.0.86 → ok-script-0.0.96}/ok/util/thread.py +0 -0
  178. {ok-script-0.0.86 → ok-script-0.0.96}/ok/util/win32_process.py +0 -0
  179. {ok-script-0.0.86 → ok-script-0.0.96}/ok/util/yaml.py +0 -0
  180. {ok-script-0.0.86 → ok-script-0.0.96}/ok_script.egg-info/dependency_links.txt +0 -0
  181. {ok-script-0.0.86 → ok-script-0.0.96}/ok_script.egg-info/requires.txt +0 -0
  182. {ok-script-0.0.86 → ok-script-0.0.96}/ok_script.egg-info/top_level.txt +0 -0
  183. {ok-script-0.0.86 → ok-script-0.0.96}/setup.cfg +0 -0
  184. {ok-script-0.0.86 → ok-script-0.0.96}/tests/__init__.py +0 -0
  185. {ok-script-0.0.86 → ok-script-0.0.96}/tests/test_updater.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ok-script
3
- Version: 0.0.86
3
+ Version: 0.0.96
4
4
  Summary: Automation with Computer Vision for Python
5
5
  Home-page: https://github.com/ok-oldking/ok-script
6
6
  Author: ok-oldking
@@ -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 = results[0]
180
+ biggest = None
182
181
  for result in results:
183
- from ok.capture.windows.BitBltCaptureMethod import bit_blt_test_hwnd
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=5)
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
- break
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=True)
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.capture.windows.window import get_window_bounds
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
- def bit_blt_test_hwnd(hwnd):
65
- x, y, border, title_height, width, height, scaling, ext_left_bounds, ext_top_bounds = get_window_bounds(
66
- hwnd)
67
- frame = bit_blt_capture_frame(hwnd, border, title_height, width, height, ext_left_bounds, ext_top_bounds)
68
- return frame is not None
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
- last_frame = None
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
- frame = self.frame_pool.TryGetNextFrame()
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
- self.last_frame_time = time.time()
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
- if need_reset_framepool:
58
- self.reset_framepool(frame.ContentSize)
59
- return
60
- tex = None
70
+ tex = None
61
71
 
62
- cputex = None
63
- try:
64
- from ok.rotypes.Windows.Graphics.DirectX.Direct3D11 import IDirect3DDxgiInterfaceAccess
65
- from ok.rotypes.roapi import GetActivationFactory
66
- tex = frame.Surface.astype(IDirect3DDxgiInterfaceAccess).GetInterface(
67
- d3d11.ID3D11Texture2D.GUID).astype(d3d11.ID3D11Texture2D)
68
- desc = tex.GetDesc()
69
- desc2 = d3d11.D3D11_TEXTURE2D_DESC()
70
- desc2.Width = desc.Width
71
- desc2.Height = desc.Height
72
- desc2.MipLevels = desc.MipLevels
73
- desc2.ArraySize = desc.ArraySize
74
- desc2.Format = desc.Format
75
- desc2.SampleDesc = desc.SampleDesc
76
- desc2.Usage = d3d11.D3D11_USAGE_STAGING
77
- desc2.CPUAccessFlags = d3d11.D3D11_CPU_ACCESS_READ
78
- desc2.BindFlags = 0
79
- desc2.MiscFlags = 0
80
- cputex = self.dxdevice.CreateTexture2D(ctypes.byref(desc2), None)
81
- self.immediatedc.CopyResource(cputex, tex)
82
- mapinfo = self.immediatedc.Map(cputex, 0, d3d11.D3D11_MAP_READ, 0)
83
- img = np.ctypeslib.as_array(ctypes.cast(mapinfo.pData, PBYTE),
84
- (desc.Height, mapinfo.RowPitch // 4, 4))[
85
- :, :desc.Width].copy()
86
- self.immediatedc.Unmap(cputex, 0)
87
- except OSError as e:
88
- if e.winerror == d3d11.DXGI_ERROR_DEVICE_REMOVED or e.winerror == d3d11.DXGI_ERROR_DEVICE_RESET:
89
- need_reset_framepool = True
90
- need_reset_device = True
91
- else:
92
- raise
93
- finally:
94
- if tex is not None:
95
- tex.Release()
96
- if cputex is not None:
97
- cputex.Release()
98
- if need_reset_framepool:
99
- self.reset_framepool(frame.ContentSize, need_reset_device)
100
- return self.get_frame()
101
- self.last_frame = img
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
- 1, item.Size)
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.last_frame
197
- self.last_frame = None
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 > 1:
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, 1, size)
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, width: int, height: int) -> None:
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
- self.width = width
20
- self.height = height
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}'