calango 2.2.6__tar.gz → 2.2.8__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: calango
3
- Version: 2.2.6
3
+ Version: 2.2.8
4
4
  Summary: It looks like calango
5
5
  Home-page: https://github.com/cereja-project/calango
6
6
  Author: Joab Leite
@@ -11,6 +11,10 @@ Classifier: Operating System :: OS Independent
11
11
  Requires-Python: >=3.6
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
+ Requires-Dist: cereja
15
+ Requires-Dist: opencv-python
16
+ Requires-Dist: matplotlib
17
+ Requires-Dist: numpy
14
18
 
15
19
  # Calango Project
16
20
 
@@ -26,5 +26,5 @@ from . import settings
26
26
  from .devices import Mouse
27
27
  from .media import Image, VideoWriter, Video
28
28
 
29
- VERSION = "2.2.6.final.0"
29
+ VERSION = "2.2.8.final.0"
30
30
  __version__ = get_version_pep440_compliant(VERSION)
@@ -6,7 +6,7 @@ import subprocess
6
6
  import threading
7
7
  import time
8
8
  from abc import abstractmethod
9
- from typing import Union, Tuple, Sequence, Iterator
9
+ from typing import Union, Tuple, Sequence, Iterator, List
10
10
  from urllib.parse import urlparse
11
11
  import cereja as cj
12
12
  import cv2
@@ -33,6 +33,14 @@ def is_url(val):
33
33
  class Image(np.ndarray):
34
34
  _GRAY_SCALE = 'GRAY_SCALE'
35
35
  _color_mode = 'BGR'
36
+ COLORS_RANGE = {
37
+ 'red': (np.array([0, 0, 100]), np.array([80, 80, 255])),
38
+ 'green': (np.array([0, 100, 0]), np.array([80, 255, 80])),
39
+ 'blue': (np.array([100, 0, 0]), np.array([255, 80, 80])),
40
+ 'yellow': (np.array([0, 100, 100]), np.array([80, 255, 255])),
41
+ 'cyan': (np.array([100, 100, 0]), np.array([255, 255, 80])),
42
+ 'magenta': (np.array([100, 0, 100]), np.array([255, 80, 255])),
43
+ }
36
44
 
37
45
  def __new__(cls, im: Union[str, np.ndarray] = None, color_mode: str = 'BGR', shape=None, dtype=None,
38
46
  **kwargs) -> 'Image':
@@ -99,6 +107,129 @@ class Image(np.ndarray):
99
107
  def set_border(self, size: int = 1, color: Tuple[int, int, int] = (0, 255, 0)):
100
108
  cv2.rectangle(self, (size, size), (self.width - 1, self.height - 1), color, size)
101
109
 
110
+ def color_range(self, color: str):
111
+ assert color in self.COLORS_RANGE, f'Color {color} is not valid.'
112
+ return self.COLORS_RANGE[color]
113
+
114
+ def get_color_mask(self, lower, upper):
115
+ return cv2.inRange(self, lower, upper)
116
+
117
+ @staticmethod
118
+ def calculate_color_percentage(img, lower_bgr, upper_bgr):
119
+
120
+ # Create a mask with the pixels that fall within the color range
121
+ color_mask = cv2.inRange(img, lower_bgr, upper_bgr)
122
+
123
+ # Count the number of pixels in the color range
124
+ color_pixel_count = np.count_nonzero(color_mask)
125
+
126
+ # Count the total number of pixels in the image
127
+ total_pixels = img.shape[0] * img.shape[1]
128
+
129
+ # Calculate the percentage of the color
130
+ color_percentage = (color_pixel_count / total_pixels) * 100
131
+ return color_percentage
132
+
133
+ def get_strides(self, kernel_size, strides=1):
134
+ """
135
+ Returns batches of fixed window size (kernel_size) with a given stride
136
+ @param kernel_size: window size
137
+ @param strides: default is 1
138
+ """
139
+ for i in range(0, self.shape[0] - kernel_size + 1, strides):
140
+ for j in range(0, self.shape[1] - kernel_size + 1, strides):
141
+ yield self[i:i + kernel_size, j:j + kernel_size]
142
+
143
+ @property
144
+ def blue_percentage(self):
145
+ return self.calculate_color_percentage(self, *self.color_range('blue'))
146
+
147
+ @property
148
+ def green_percentage(self):
149
+ return self.calculate_color_percentage(self, *self.color_range('green'))
150
+
151
+ @property
152
+ def red_percentage(self):
153
+ return self.calculate_color_percentage(self, *self.color_range('red'))
154
+
155
+ @property
156
+ def yellow_percentage(self):
157
+ return self.calculate_color_percentage(self, *self.color_range('yellow'))
158
+
159
+ @property
160
+ def cyan_percentage(self):
161
+ return self.calculate_color_percentage(self, *self.color_range('cyan'))
162
+
163
+ @property
164
+ def magenta_percentage(self):
165
+ return self.calculate_color_percentage(self, *self.color_range('magenta'))
166
+
167
+ def hex_to_bgr(self, hex_color):
168
+ return tuple(int(hex_color[i:i + 2], 16) for i in (0, 2, 4))
169
+
170
+ def bgr_to_hex(self, bgr_color):
171
+ return '%02x%02x%02x' % bgr_color
172
+
173
+ def parse_color(self, color):
174
+ if isinstance(color, str):
175
+ if color.startswith('#'):
176
+ return self.hex_to_bgr(color[1:])
177
+ return self.hex_to_bgr(color)
178
+ return color
179
+
180
+ def get_color_percentage(self, min_color, max_color):
181
+ return self.calculate_color_percentage(self, self.parse_color(min_color), self.parse_color(max_color))
182
+
183
+ def similarity(self, image: Image, method=cv2.TM_CCOEFF_NORMED):
184
+ result = cv2.matchTemplate(self, image, method)
185
+ _, max_val, _, max_loc = cv2.minMaxLoc(result)
186
+ return max_val
187
+
188
+ def _padding_points(self, points, distance: 0):
189
+ if not len(points):
190
+ return []
191
+ points = cj.geolinear.find_best_locations(points, distance)
192
+ return points
193
+
194
+ def _match_template(self, template: np.ndarray, method=cv2.TM_CCOEFF_NORMED, threshold=None, all_locations=False,
195
+ distance_between_points=1):
196
+ threshold = threshold or 0.0
197
+ result = []
198
+ res = cv2.matchTemplate(self, template, method)
199
+ if all_locations:
200
+ loc = np.where(res >= threshold)
201
+ for n, pt in enumerate(zip(*loc[::-1])):
202
+ acc = res[pt[1], pt[0]]
203
+ result.append([int(pt[0] + template.shape[1] // 2), int(pt[1] + template.shape[0] // 2), acc])
204
+ else:
205
+ _, max_val, _, max_loc = cv2.minMaxLoc(res)
206
+ if max_val >= threshold:
207
+ result.append(
208
+ [int(max_loc[0] + template.shape[1] // 2), int(max_loc[1] + template.shape[0] // 2), max_val])
209
+
210
+ if distance_between_points > 1:
211
+ result = self._padding_points(result, distance_between_points)
212
+ return result
213
+
214
+ def match_template(self, template: Union[np.ndarray, List[np.ndarray]], method=cv2.TM_CCOEFF_NORMED,
215
+ threshold=None, all_locations=False, distance_between_points=1):
216
+ threshold = threshold or 0.0
217
+
218
+ result = []
219
+ if not isinstance(template, list):
220
+ template = [template]
221
+ for template in template:
222
+ res = self._match_template(template, method, threshold, all_locations=all_locations,
223
+ distance_between_points=distance_between_points)
224
+ result.extend(res)
225
+ return result
226
+
227
+ def find_locations_by_color(self, color, distance_between_points=1):
228
+
229
+ mask = self.get_color_mask(*self.color_range(color))
230
+ positions = np.where(mask == 255)[::-1]
231
+ return self._padding_points(list(zip(*positions)), distance_between_points)
232
+
102
233
  @property
103
234
  def mask(self):
104
235
  return Image(np.zeros(self.shape[:2], dtype=np.uint8))
@@ -258,6 +389,16 @@ class Image(np.ndarray):
258
389
  return Image(cv2.cvtColor(self, code_))
259
390
  return self
260
391
 
392
+ def gray_to_bgr(self) -> Image:
393
+ self[:, ] = cv2.cvtColor(self, cv2.COLOR_GRAY2BGR)
394
+ self._color_mode = "BGR"
395
+ return self
396
+
397
+ def gray_to_rgb(self) -> Image:
398
+ self[:, ] = cv2.cvtColor(self, cv2.COLOR_GRAY2RGB)
399
+ self._color_mode = "RGB"
400
+ return self
401
+
261
402
  def rgb_to_bgr(self) -> Image:
262
403
  if self._color_mode == 'RGB':
263
404
  self._color_mode = 'BGR'
@@ -285,6 +426,14 @@ class Image(np.ndarray):
285
426
  180: cv2.ROTATE_180
286
427
  }.get(degrees))
287
428
 
429
+ def put_on_center(self, img):
430
+ img = Image(img)
431
+ x, y = self.center_position
432
+ x1 = x - img.width // 2
433
+ y1 = y - img.height // 2
434
+ self[y1:y1 + img.height, x1: x1 + img.width] = img
435
+ return self
436
+
288
437
  def crop_by_center(self, size=None, keep_scale=False) -> Image:
289
438
  assert size is None or isinstance(size, (list, tuple)) and cj.is_numeric_sequence(size) and len(
290
439
  size) == 2, 'Send HxW image cropped output'
@@ -307,6 +456,24 @@ class Image(np.ndarray):
307
456
 
308
457
  return Image(cv2.resize(im_crop[start[1]:end[1], start[0]:end[0]], (new_w, new_h)))
309
458
 
459
+ @staticmethod
460
+ def remove_shadow(image):
461
+ if image.shape[-1] > 1:
462
+ image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
463
+
464
+ # 2. Apply bilateral filter to smooth the image
465
+ bilateral = cv2.bilateralFilter(image, 15, 75, 75)
466
+
467
+ # 3. Compute the background using morphological closing
468
+ kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 21))
469
+ background = cv2.morphologyEx(bilateral, cv2.MORPH_CLOSE, kernel)
470
+
471
+ # 4. Compute the ratio of the grayscale and background image to remove shadows
472
+ ratio = (image.astype('float') / background.astype('float')) * 255.0
473
+ shadow_removed = np.clip(ratio, 0, 255).astype('uint8')
474
+
475
+ return shadow_removed
476
+
310
477
  def prune(self) -> Image:
311
478
  min_len = self.min_len
312
479
  return Image(self.crop_by_center((min_len, min_len)))
@@ -317,6 +484,10 @@ class Image(np.ndarray):
317
484
  cv2.circle(self, position, radius, color, thickness)
318
485
  return self
319
486
 
487
+ def rect(self, start, end, color=(0, 0, 0), thickness=1):
488
+ cv2.rectangle(self, start, end, color, thickness)
489
+ return self
490
+
320
491
  def get_mask_circle(self, radius=None, position=None):
321
492
  return self.mask.circle(radius=radius, position=position, color=(255, 255, 255), thickness=-1)
322
493
 
@@ -0,0 +1,48 @@
1
+ import base64
2
+ import io
3
+ from .settings import ON_COLAB_JUPYTER
4
+ from matplotlib import pyplot as plt
5
+ import math
6
+
7
+ __all__ = ['show_local_mp4']
8
+
9
+
10
+ def show_local_mp4(file_name, width=640, height=480):
11
+ assert ON_COLAB_JUPYTER, 'show_local_mp4 can bee used only on colab runtime'
12
+ # noinspection PyUnresolvedReferences
13
+ from IPython.display import HTML
14
+ video_encoded = base64.b64encode(io.open(file_name, "rb").read())
15
+ return HTML(
16
+ data="""<video width="{0}" height="{1}" alt="test" controls>
17
+ <source src="data:video/mp4;base64,{2}" type="video/mp4" />
18
+ </video>""".format(
19
+ width, height, video_encoded.decode("ascii")
20
+ )
21
+ )
22
+
23
+
24
+ class ImagePlot:
25
+ def __init__(self, fig_size=(13, 13), max_cols=4):
26
+ self.fig_size = fig_size
27
+ self.figure = plt.figure(figsize=fig_size) # TODO: does it need to be global?
28
+ self._max_cols = max_cols
29
+ self._total_images = 0
30
+
31
+ @property
32
+ def current_row(self):
33
+ return math.ceil(self._total_images / self._max_cols)
34
+
35
+ @property
36
+ def current_col(self):
37
+ return ((self._total_images - 1) % self._max_cols) + 1
38
+
39
+ def plot(self, image, title=None, color='gray'):
40
+ try:
41
+ self._total_images += 1
42
+ plt.subplot(self._max_cols, self._max_cols, self._total_images)
43
+ plt.title(self.current_col if title is None else title)
44
+ plt.xticks([]), plt.yticks([])
45
+ plt.imshow(image.copy(), color)
46
+ except Exception as err:
47
+ self._total_images -= 1 # garante consistencia da quantidade de imagens
48
+ raise err
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: calango
3
- Version: 2.2.6
3
+ Version: 2.2.8
4
4
  Summary: It looks like calango
5
5
  Home-page: https://github.com/cereja-project/calango
6
6
  Author: Joab Leite
@@ -11,6 +11,10 @@ Classifier: Operating System :: OS Independent
11
11
  Requires-Python: >=3.6
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
+ Requires-Dist: cereja
15
+ Requires-Dist: opencv-python
16
+ Requires-Dist: matplotlib
17
+ Requires-Dist: numpy
14
18
 
15
19
  # Calango Project
16
20
 
@@ -1,6 +1,4 @@
1
1
  cereja
2
- pyautogui
3
2
  opencv-python
4
3
  matplotlib
5
4
  numpy
6
- mss
@@ -1,19 +0,0 @@
1
- import base64
2
- import io
3
- from .settings import ON_COLAB_JUPYTER
4
-
5
- __all__ = ['show_local_mp4']
6
-
7
-
8
- def show_local_mp4(file_name, width=640, height=480):
9
- assert ON_COLAB_JUPYTER, 'show_local_mp4 can bee used only on colab runtime'
10
- # noinspection PyUnresolvedReferences
11
- from IPython.display import HTML
12
- video_encoded = base64.b64encode(io.open(file_name, "rb").read())
13
- return HTML(
14
- data="""<video width="{0}" height="{1}" alt="test" controls>
15
- <source src="data:video/mp4;base64,{2}" type="video/mp4" />
16
- </video>""".format(
17
- width, height, video_encoded.decode("ascii")
18
- )
19
- )
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes