py2ls 0.1.9.7__py3-none-any.whl → 0.1.9.9__py3-none-any.whl

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.
py2ls/ips.py CHANGED
@@ -160,10 +160,10 @@ def search(
160
160
  kind="text",
161
161
  output="df",
162
162
  verbose=False,
163
- download=True,
163
+ download=False,
164
164
  dir_save=dir_save,
165
+ **kwargs,
165
166
  ):
166
- from duckduckgo_search import DDGS
167
167
 
168
168
  if "te" in kind.lower():
169
169
  results = DDGS().text(query, max_results=limit)
@@ -173,8 +173,8 @@ def search(
173
173
  print(f'searching "{query}": got the results below\n{res}')
174
174
  if download:
175
175
  try:
176
- netfinder.downloader(
177
- url=res.links.tolist(), dir_save=dir_save, verbose=verbose
176
+ downloader(
177
+ url=res.links.tolist(), dir_save=dir_save, verbose=verbose, **kwargs
178
178
  )
179
179
  except:
180
180
  if verbose:
@@ -1146,6 +1146,7 @@ def fload(fpath, kind=None, **kwargs):
1146
1146
  "spider",
1147
1147
  "tga",
1148
1148
  "tiff",
1149
+ "tif",
1149
1150
  "webp",
1150
1151
  "json",
1151
1152
  ]
@@ -2067,223 +2068,391 @@ def apply_filter(img, *args):
2067
2068
  return img.filter(supported_filters[filter_name])
2068
2069
 
2069
2070
 
2070
- # def imgsetss(
2071
- # img,
2072
- # sets=None,
2073
- # show=True,
2074
- # show_axis=False,
2075
- # size=None,
2076
- # dpi=100,
2077
- # figsize=None,
2078
- # auto=False,
2079
- # filter_kws=None,
2080
- # ):
2081
- # """
2082
- # Apply various enhancements and filters to an image using PIL's ImageEnhance and ImageFilter modules.
2083
-
2084
- # Args:
2085
- # img (PIL.Image): The input image.
2086
- # sets (dict): A dictionary specifying the enhancements, filters, and their parameters.
2087
- # show (bool): Whether to display the enhanced image.
2088
- # show_axis (bool): Whether to display axes on the image plot.
2089
- # size (tuple): The size of the thumbnail, cover, contain, or fit operation.
2090
- # dpi (int): Dots per inch for the displayed image.
2091
- # figsize (tuple): The size of the figure for displaying the image.
2092
- # auto (bool): Whether to automatically enhance the image based on its characteristics.
2093
-
2094
- # Returns:
2095
- # PIL.Image: The enhanced image.
2096
-
2097
- # Supported enhancements and filters:
2098
- # - "sharpness": Adjusts the sharpness of the image. Values > 1 increase sharpness, while values < 1 decrease sharpness.
2099
- # - "contrast": Adjusts the contrast of the image. Values > 1 increase contrast, while values < 1 decrease contrast.
2100
- # - "brightness": Adjusts the brightness of the image. Values > 1 increase brightness, while values < 1 decrease brightness.
2101
- # - "color": Adjusts the color saturation of the image. Values > 1 increase saturation, while values < 1 decrease saturation.
2102
- # - "rotate": Rotates the image by the specified angle.
2103
- # - "crop" or "cut": Crops the image. The value should be a tuple specifying the crop box as (left, upper, right, lower).
2104
- # - "size": Resizes the image to the specified dimensions.
2105
- # - "thumbnail": Resizes the image to fit within the given size while preserving aspect ratio.
2106
- # - "cover": Resizes and crops the image to fill the specified size.
2107
- # - "contain": Resizes the image to fit within the specified size, adding borders if necessary.
2108
- # - "fit": Resizes and pads the image to fit within the specified size.
2109
- # - "filter": Applies various filters to the image (e.g., BLUR, CONTOUR, EDGE_ENHANCE).
2110
-
2111
- # Note:
2112
- # The "color" and "enhance" enhancements are not implemented in this function.
2113
- # """
2114
- # supported_filters = [
2115
- # "BLUR",
2116
- # "CONTOUR",
2117
- # "DETAIL",
2118
- # "EDGE_ENHANCE",
2119
- # "EDGE_ENHANCE_MORE",
2120
- # "EMBOSS",
2121
- # "FIND_EDGES",
2122
- # "SHARPEN",
2123
- # "SMOOTH",
2124
- # "SMOOTH_MORE",
2125
- # "MIN_FILTER",
2126
- # "MAX_FILTER",
2127
- # "MODE_FILTER",
2128
- # "MULTIBAND_FILTER",
2129
- # "GAUSSIAN_BLUR",
2130
- # "BOX_BLUR",
2131
- # "MEDIAN_FILTER",
2132
- # ]
2133
- # print("sets: a dict,'sharp:1.2','color','contrast:'auto' or 1.2','bright', 'crop: x_upperleft,y_upperleft, x_lowerright, y_lowerright','rotation','resize','rem or background'")
2134
- # print(f"usage: filter_kws 'dict' below:")
2135
- # pp([str(i).lower() for i in supported_filters])
2136
- # print("\nlog:\n")
2137
- # def confirm_rembg_models(model_name):
2138
- # models_support = [
2139
- # "u2net",
2140
- # "u2netp",
2141
- # "u2net_human_seg",
2142
- # "u2net_cloth_seg",
2143
- # "silueta",
2144
- # "isnet-general-use",
2145
- # "isnet-anime",
2146
- # "sam",
2147
- # ]
2148
- # if model_name in models_support:
2149
- # print(f"model_name: {model_name}")
2150
- # return model_name
2151
- # else:
2152
- # print(f"{model_name} cannot be found, check the name:{models_support}, default('isnet-general-use') has been used")
2153
- # return "isnet-general-use"
2154
- # def auto_enhance(img):
2155
- # """
2156
- # Automatically enhances the image based on its characteristics.
2157
- # Args:
2158
- # img (PIL.Image): The input image.
2159
- # Returns:
2160
- # dict: A dictionary containing the optimal enhancement values.
2161
- # """
2162
- # # Determine the bit depth based on the image mode
2163
- # if img.mode in ["1", "L", "P", "RGB", "YCbCr", "LAB", "HSV"]:
2164
- # # 8-bit depth per channel
2165
- # bit_depth = 8
2166
- # elif img.mode in ["RGBA", "CMYK"]:
2167
- # # 8-bit depth per channel + alpha (RGBA) or additional channels (CMYK)
2168
- # bit_depth = 8
2169
- # elif img.mode in ["I", "F"]:
2170
- # # 16-bit depth per channel (integer or floating-point)
2171
- # bit_depth = 16
2172
- # else:
2173
- # raise ValueError("Unsupported image mode")
2174
- # # Calculate the brightness and contrast for each channel
2175
- # num_channels = len(img.getbands())
2176
- # brightness_factors = []
2177
- # contrast_factors = []
2178
- # for channel in range(num_channels):
2179
- # channel_histogram = img.split()[channel].histogram()
2180
- # brightness = sum(i * w for i, w in enumerate(channel_histogram))/sum(channel_histogram)
2181
- # channel_min, channel_max = img.split()[channel].getextrema()
2182
- # contrast = channel_max - channel_min
2183
- # # Adjust calculations based on bit depth
2184
- # normalization_factor = 2**bit_depth - 1 # Max value for the given bit depth
2185
- # brightness_factor = (1.0 + (brightness - normalization_factor / 2) / normalization_factor)
2186
- # contrast_factor = (1.0 + (contrast - normalization_factor / 2) / normalization_factor)
2187
- # brightness_factors.append(brightness_factor)
2188
- # contrast_factors.append(contrast_factor)
2189
- # # Calculate the average brightness and contrast factors across channels
2190
- # avg_brightness_factor = sum(brightness_factors) / num_channels
2191
- # avg_contrast_factor = sum(contrast_factors) / num_channels
2192
- # return {"brightness": avg_brightness_factor, "contrast": avg_contrast_factor}
2193
- # # Load image if input is a file path
2194
- # if isinstance(img, str):
2195
- # img = load_img(img)
2196
- # img_update = img.copy()
2197
- # # Auto-enhance image if requested
2198
- # if auto:
2199
- # auto_params = auto_enhance(img_update)
2200
- # sets.update(auto_params)
2201
- # if sets is None:
2202
- # sets = {}
2203
- # for k, value in sets.items():
2204
- # if "shar" in k.lower():
2205
- # enhancer = ImageEnhance.Sharpness(img_update)
2206
- # img_update = enhancer.enhance(value)
2207
- # elif "col" in k.lower() and 'bg' not in k.lower():
2208
- # enhancer = ImageEnhance.Color(img_update)
2209
- # img_update = enhancer.enhance(value)
2210
- # elif "contr" in k.lower():
2211
- # if value and isinstance(value,(float,int)):
2212
- # enhancer = ImageEnhance.Contrast(img_update)
2213
- # img_update = enhancer.enhance(value)
2214
- # else:
2215
- # print('autocontrasted')
2216
- # img_update = ImageOps.autocontrast(img_update)
2217
- # elif "bri" in k.lower():
2218
- # enhancer = ImageEnhance.Brightness(img_update)
2219
- # img_update = enhancer.enhance(value)
2220
- # elif "cro" in k.lower() or "cut" in k.lower():
2221
- # img_update=img_update.crop(value)
2222
- # elif "rota" in k.lower():
2223
- # img_update = img_update.rotate(value)
2224
- # elif "si" in k.lower():
2225
- # img_update = img_update.resize(value)
2226
- # elif "thum" in k.lower():
2227
- # img_update.thumbnail(value)
2228
- # elif "cover" in k.lower():
2229
- # img_update = ImageOps.cover(img_update, size=value)
2230
- # elif "contain" in k.lower():
2231
- # img_update = ImageOps.contain(img_update, size=value)
2232
- # elif "fit" in k.lower():
2233
- # img_update = ImageOps.fit(img_update, size=value)
2234
- # elif "pad" in k.lower():
2235
- # img_update = ImageOps.pad(img_update, size=value)
2236
- # elif 'rem' in k.lower() or 'rm' in k.lower() or 'back' in k.lower():
2237
- # if value and isinstance(value,(int,float,list)):
2238
- # print('example usage: {"rm":[alpha_matting_background_threshold(20),alpha_matting_foreground_threshold(270),alpha_matting_erode_sive(11)]}')
2239
- # print("https://github.com/danielgatis/rembg/blob/main/USAGE.md")
2240
- # # ### Parameters:
2241
- # # data (Union[bytes, PILImage, np.ndarray]): The input image data.
2242
- # # alpha_matting (bool, optional): Flag indicating whether to use alpha matting. Defaults to False.
2243
- # # alpha_matting_foreground_threshold (int, optional): Foreground threshold for alpha matting. Defaults to 240.
2244
- # # alpha_matting_background_threshold (int, optional): Background threshold for alpha matting. Defaults to 10.
2245
- # # alpha_matting_erode_size (int, optional): Erosion size for alpha matting. Defaults to 10.
2246
- # # session (Optional[BaseSession], optional): A session object for the 'u2net' model. Defaults to None.
2247
- # # only_mask (bool, optional): Flag indicating whether to return only the binary masks. Defaults to False.
2248
- # # post_process_mask (bool, optional): Flag indicating whether to post-process the masks. Defaults to False.
2249
- # # bgcolor (Optional[Tuple[int, int, int, int]], optional): Background color for the cutout image. Defaults to None.
2250
- # # ###
2251
- # if isinstance(value,int):
2252
- # value=[value]
2253
- # if len(value) <2:
2254
- # img_update = remove(img_update,alpha_matting=True,alpha_matting_background_threshold=value)
2255
- # elif 2<=len(value)<3:
2256
- # img_update = remove(img_update,alpha_matting=True,alpha_matting_background_threshold=value[0],alpha_matting_foreground_threshold=value[1])
2257
- # elif 3<=len(value)<4:
2258
- # img_update = remove(img_update,alpha_matting=True,alpha_matting_background_threshold=value[0],alpha_matting_foreground_threshold=value[1],alpha_matting_erode_size=value[2])
2259
- # if isinstance(value,tuple): # replace the background color
2260
- # if len(value)==3:
2261
- # value+=(255,)
2262
- # img_update = remove(img_update, bgcolor=value)
2263
- # if isinstance(value,str):
2264
- # if confirm_rembg_models(value):
2265
- # img_update=remove(img_update,session=new_session(value))
2266
- # else:
2267
- # img_update=remove(img_update)
2268
- # elif 'bgcolor' in k.lower():
2269
- # if isinstance(value,list):
2270
- # value=tuple(value)
2271
- # if isinstance(value,tuple): # replace the background color
2272
- # if len(value)==3:
2273
- # value+=(255,)
2274
- # img_update = remove(img_update, bgcolor=value)
2275
- # if filter_kws:
2276
- # for filter_name, filter_value in filter_kws.items():
2277
- # img_update = apply_filter(img_update, filter_name, filter_value)
2278
- # # Display the image if requested
2279
- # if show:
2280
- # if figsize is None:
2281
- # plt.figure(dpi=dpi)
2282
- # else:
2283
- # plt.figure(figsize=figsize, dpi=dpi)
2284
- # plt.imshow(img_update)
2285
- # plt.axis("on") if show_axis else plt.axis("off")
2286
- # return img_update
2071
+ def imgsetss(
2072
+ img,
2073
+ sets=None,
2074
+ show=True,
2075
+ show_axis=False,
2076
+ size=None,
2077
+ dpi=100,
2078
+ figsize=None,
2079
+ auto=False,
2080
+ filter_kws=None,
2081
+ ):
2082
+ """
2083
+ Apply various enhancements and filters to an image using PIL's ImageEnhance and ImageFilter modules.
2084
+
2085
+ Args:
2086
+ img (PIL.Image): The input image.
2087
+ sets (dict): A dictionary specifying the enhancements, filters, and their parameters.
2088
+ show (bool): Whether to display the enhanced image.
2089
+ show_axis (bool): Whether to display axes on the image plot.
2090
+ size (tuple): The size of the thumbnail, cover, contain, or fit operation.
2091
+ dpi (int): Dots per inch for the displayed image.
2092
+ figsize (tuple): The size of the figure for displaying the image.
2093
+ auto (bool): Whether to automatically enhance the image based on its characteristics.
2094
+
2095
+ Returns:
2096
+ PIL.Image: The enhanced image.
2097
+
2098
+ Supported enhancements and filters:
2099
+ - "sharpness": Adjusts the sharpness of the image. Values > 1 increase sharpness, while values < 1 decrease sharpness.
2100
+ - "contrast": Adjusts the contrast of the image. Values > 1 increase contrast, while values < 1 decrease contrast.
2101
+ - "brightness": Adjusts the brightness of the image. Values > 1 increase brightness, while values < 1 decrease brightness.
2102
+ - "color": Adjusts the color saturation of the image. Values > 1 increase saturation, while values < 1 decrease saturation.
2103
+ - "rotate": Rotates the image by the specified angle.
2104
+ - "crop" or "cut": Crops the image. The value should be a tuple specifying the crop box as (left, upper, right, lower).
2105
+ - "size": Resizes the image to the specified dimensions.
2106
+ - "thumbnail": Resizes the image to fit within the given size while preserving aspect ratio.
2107
+ - "cover": Resizes and crops the image to fill the specified size.
2108
+ - "contain": Resizes the image to fit within the specified size, adding borders if necessary.
2109
+ - "fit": Resizes and pads the image to fit within the specified size.
2110
+ - "filter": Applies various filters to the image (e.g., BLUR, CONTOUR, EDGE_ENHANCE).
2111
+
2112
+ Note:
2113
+ The "color" and "enhance" enhancements are not implemented in this function.
2114
+ """
2115
+ supported_filters = [
2116
+ "BLUR",
2117
+ "CONTOUR",
2118
+ "DETAIL",
2119
+ "EDGE_ENHANCE",
2120
+ "EDGE_ENHANCE_MORE",
2121
+ "EMBOSS",
2122
+ "FIND_EDGES",
2123
+ "SHARPEN",
2124
+ "SMOOTH",
2125
+ "SMOOTH_MORE",
2126
+ "MIN_FILTER",
2127
+ "MAX_FILTER",
2128
+ "MODE_FILTER",
2129
+ "MULTIBAND_FILTER",
2130
+ "GAUSSIAN_BLUR",
2131
+ "BOX_BLUR",
2132
+ "MEDIAN_FILTER",
2133
+ ]
2134
+ print(
2135
+ "sets: a dict,'sharp:1.2','color','contrast:'auto' or 1.2','bright', 'crop: x_upperleft,y_upperleft, x_lowerright, y_lowerright','rotation','resize','rem or background'"
2136
+ )
2137
+ print(f"usage: filter_kws 'dict' below:")
2138
+ pp([str(i).lower() for i in supported_filters])
2139
+ print("\nlog:\n")
2140
+
2141
+ def confirm_rembg_models(model_name):
2142
+ models_support = [
2143
+ "u2net",
2144
+ "u2netp",
2145
+ "u2net_human_seg",
2146
+ "u2net_cloth_seg",
2147
+ "silueta",
2148
+ "isnet-general-use",
2149
+ "isnet-anime",
2150
+ "sam",
2151
+ ]
2152
+ if model_name in models_support:
2153
+ print(f"model_name: {model_name}")
2154
+ return model_name
2155
+ else:
2156
+ print(
2157
+ f"{model_name} cannot be found, check the name:{models_support}, default('isnet-general-use') has been used"
2158
+ )
2159
+ return "isnet-general-use"
2160
+
2161
+ def auto_enhance(img):
2162
+ """
2163
+ Automatically enhances the image based on its characteristics.
2164
+ Args:
2165
+ img (PIL.Image): The input image.
2166
+ Returns:
2167
+ dict: A dictionary containing the optimal enhancement values.
2168
+ """
2169
+ # Determine the bit depth based on the image mode
2170
+ if img.mode in ["1", "L", "P", "RGB", "YCbCr", "LAB", "HSV"]:
2171
+ # 8-bit depth per channel
2172
+ bit_depth = 8
2173
+ elif img.mode in ["RGBA", "CMYK"]:
2174
+ # 8-bit depth per channel + alpha (RGBA) or additional channels (CMYK)
2175
+ bit_depth = 8
2176
+ elif img.mode in ["I", "F"]:
2177
+ # 16-bit depth per channel (integer or floating-point)
2178
+ bit_depth = 16
2179
+ else:
2180
+ raise ValueError("Unsupported image mode")
2181
+ # Calculate the brightness and contrast for each channel
2182
+ num_channels = len(img.getbands())
2183
+ brightness_factors = []
2184
+ contrast_factors = []
2185
+ for channel in range(num_channels):
2186
+ channel_histogram = img.split()[channel].histogram()
2187
+ brightness = sum(i * w for i, w in enumerate(channel_histogram)) / sum(
2188
+ channel_histogram
2189
+ )
2190
+ channel_min, channel_max = img.split()[channel].getextrema()
2191
+ contrast = channel_max - channel_min
2192
+ # Adjust calculations based on bit depth
2193
+ normalization_factor = 2**bit_depth - 1 # Max value for the given bit depth
2194
+ brightness_factor = (
2195
+ 1.0 + (brightness - normalization_factor / 2) / normalization_factor
2196
+ )
2197
+ contrast_factor = (
2198
+ 1.0 + (contrast - normalization_factor / 2) / normalization_factor
2199
+ )
2200
+ brightness_factors.append(brightness_factor)
2201
+ contrast_factors.append(contrast_factor)
2202
+ # Calculate the average brightness and contrast factors across channels
2203
+ avg_brightness_factor = sum(brightness_factors) / num_channels
2204
+ avg_contrast_factor = sum(contrast_factors) / num_channels
2205
+ return {"brightness": avg_brightness_factor, "contrast": avg_contrast_factor}
2206
+
2207
+ # Load image if input is a file path
2208
+ if isinstance(img, str):
2209
+ img = load_img(img)
2210
+ img_update = img.copy()
2211
+ # Auto-enhance image if requested
2212
+ if auto:
2213
+ auto_params = auto_enhance(img_update)
2214
+ sets.update(auto_params)
2215
+ if sets is None:
2216
+ sets = {}
2217
+ for k, value in sets.items():
2218
+ if "shar" in k.lower():
2219
+ enhancer = ImageEnhance.Sharpness(img_update)
2220
+ img_update = enhancer.enhance(value)
2221
+ elif "col" in k.lower() and "bg" not in k.lower():
2222
+ enhancer = ImageEnhance.Color(img_update)
2223
+ img_update = enhancer.enhance(value)
2224
+ elif "contr" in k.lower():
2225
+ if value and isinstance(value, (float, int)):
2226
+ enhancer = ImageEnhance.Contrast(img_update)
2227
+ img_update = enhancer.enhance(value)
2228
+ else:
2229
+ print("autocontrasted")
2230
+ img_update = ImageOps.autocontrast(img_update)
2231
+ elif "bri" in k.lower():
2232
+ enhancer = ImageEnhance.Brightness(img_update)
2233
+ img_update = enhancer.enhance(value)
2234
+ elif "cro" in k.lower() or "cut" in k.lower():
2235
+ img_update = img_update.crop(value)
2236
+ elif "rota" in k.lower():
2237
+ img_update = img_update.rotate(value)
2238
+ elif "si" in k.lower():
2239
+ img_update = img_update.resize(value)
2240
+ elif "thum" in k.lower():
2241
+ img_update.thumbnail(value)
2242
+ elif "cover" in k.lower():
2243
+ img_update = ImageOps.cover(img_update, size=value)
2244
+ elif "contain" in k.lower():
2245
+ img_update = ImageOps.contain(img_update, size=value)
2246
+ elif "fit" in k.lower():
2247
+ img_update = ImageOps.fit(img_update, size=value)
2248
+ elif "pad" in k.lower():
2249
+ img_update = ImageOps.pad(img_update, size=value)
2250
+ elif "rem" in k.lower() or "rm" in k.lower() or "back" in k.lower():
2251
+ if value and isinstance(value, (int, float, list)):
2252
+ print(
2253
+ 'example usage: {"rm":[alpha_matting_background_threshold(20),alpha_matting_foreground_threshold(270),alpha_matting_erode_sive(11)]}'
2254
+ )
2255
+ print("https://github.com/danielgatis/rembg/blob/main/USAGE.md")
2256
+ # ### Parameters:
2257
+ # data (Union[bytes, PILImage, np.ndarray]): The input image data.
2258
+ # alpha_matting (bool, optional): Flag indicating whether to use alpha matting. Defaults to False.
2259
+ # alpha_matting_foreground_threshold (int, optional): Foreground threshold for alpha matting. Defaults to 240.
2260
+ # alpha_matting_background_threshold (int, optional): Background threshold for alpha matting. Defaults to 10.
2261
+ # alpha_matting_erode_size (int, optional): Erosion size for alpha matting. Defaults to 10.
2262
+ # session (Optional[BaseSession], optional): A session object for the 'u2net' model. Defaults to None.
2263
+ # only_mask (bool, optional): Flag indicating whether to return only the binary masks. Defaults to False.
2264
+ # post_process_mask (bool, optional): Flag indicating whether to post-process the masks. Defaults to False.
2265
+ # bgcolor (Optional[Tuple[int, int, int, int]], optional): Background color for the cutout image. Defaults to None.
2266
+ # ###
2267
+ if isinstance(value, int):
2268
+ value = [value]
2269
+ if len(value) < 2:
2270
+ img_update = remove(
2271
+ img_update,
2272
+ alpha_matting=True,
2273
+ alpha_matting_background_threshold=value,
2274
+ )
2275
+ elif 2 <= len(value) < 3:
2276
+ img_update = remove(
2277
+ img_update,
2278
+ alpha_matting=True,
2279
+ alpha_matting_background_threshold=value[0],
2280
+ alpha_matting_foreground_threshold=value[1],
2281
+ )
2282
+ elif 3 <= len(value) < 4:
2283
+ img_update = remove(
2284
+ img_update,
2285
+ alpha_matting=True,
2286
+ alpha_matting_background_threshold=value[0],
2287
+ alpha_matting_foreground_threshold=value[1],
2288
+ alpha_matting_erode_size=value[2],
2289
+ )
2290
+ if isinstance(value, tuple): # replace the background color
2291
+ if len(value) == 3:
2292
+ value += (255,)
2293
+ img_update = remove(img_update, bgcolor=value)
2294
+ if isinstance(value, str):
2295
+ if confirm_rembg_models(value):
2296
+ img_update = remove(img_update, session=new_session(value))
2297
+ else:
2298
+ img_update = remove(img_update)
2299
+ elif "bgcolor" in k.lower():
2300
+ if isinstance(value, list):
2301
+ value = tuple(value)
2302
+ if isinstance(value, tuple): # replace the background color
2303
+ if len(value) == 3:
2304
+ value += (255,)
2305
+ img_update = remove(img_update, bgcolor=value)
2306
+ if filter_kws:
2307
+ for filter_name, filter_value in filter_kws.items():
2308
+ img_update = apply_filter(img_update, filter_name, filter_value)
2309
+ # Display the image if requested
2310
+ if show:
2311
+ if figsize is None:
2312
+ plt.figure(dpi=dpi)
2313
+ else:
2314
+ plt.figure(figsize=figsize, dpi=dpi)
2315
+ plt.imshow(img_update)
2316
+ plt.axis("on") if show_axis else plt.axis("off")
2317
+ return img_update
2318
+
2319
+
2320
+ from sklearn.decomposition import PCA
2321
+ from skimage import transform, feature, filters, measure
2322
+ from skimage.color import rgb2gray
2323
+ from scipy.fftpack import fftshift, fft2
2324
+ import numpy as np
2325
+ import cv2 # Used for template matching
2326
+
2327
+
2328
+ def crop_black_borders(image):
2329
+ """Crop the black borders from a rotated image."""
2330
+ # Convert the image to grayscale if it's not already
2331
+ if image.ndim == 3:
2332
+ gray_image = color.rgb2gray(image)
2333
+ else:
2334
+ gray_image = image
2335
+
2336
+ # Find all the non-black (non-zero) pixels
2337
+ mask = gray_image > 0 # Mask for non-black pixels (assuming black is zero)
2338
+ coords = np.column_stack(np.where(mask))
2339
+
2340
+ # Get the bounding box of non-black pixels
2341
+ if coords.any(): # Check if there are any non-black pixels
2342
+ y_min, x_min = coords.min(axis=0)
2343
+ y_max, x_max = coords.max(axis=0)
2344
+
2345
+ # Crop the image to the bounding box
2346
+ cropped_image = image[y_min : y_max + 1, x_min : x_max + 1]
2347
+ else:
2348
+ # If the image is completely black (which shouldn't happen), return the original image
2349
+ cropped_image = image
2350
+
2351
+ return cropped_image
2352
+
2353
+
2354
+ def detect_angle(image, by="median", template=None):
2355
+ """Detect the angle of rotation using various methods."""
2356
+ # Convert to grayscale
2357
+ gray_image = rgb2gray(image)
2358
+
2359
+ # Detect edges using Canny edge detector
2360
+ edges = feature.canny(gray_image, sigma=2)
2361
+
2362
+ # Use Hough transform to detect lines
2363
+ lines = transform.probabilistic_hough_line(edges)
2364
+
2365
+ if not lines and any(["me" in by, "pca" in by]):
2366
+ print("No lines detected. Adjust the edge detection parameters.")
2367
+ return 0
2368
+
2369
+ # Hough Transform-based angle detection (Median/Mean)
2370
+ if "me" in by:
2371
+ angles = []
2372
+ for line in lines:
2373
+ (x0, y0), (x1, y1) = line
2374
+ angle = np.arctan2(y1 - y0, x1 - x0) * 180 / np.pi
2375
+ if 80 < abs(angle) < 100:
2376
+ angles.append(angle)
2377
+ if not angles:
2378
+ return 0
2379
+ if "di" in by:
2380
+ median_angle = np.median(angles)
2381
+ rotation_angle = (
2382
+ 90 - median_angle if median_angle > 0 else -90 - median_angle
2383
+ )
2384
+
2385
+ return rotation_angle
2386
+ else:
2387
+ mean_angle = np.mean(angles)
2388
+ rotation_angle = 90 - mean_angle if mean_angle > 0 else -90 - mean_angle
2389
+
2390
+ return rotation_angle
2391
+
2392
+ # PCA-based angle detection
2393
+ elif "pca" in by:
2394
+ y, x = np.nonzero(edges)
2395
+ if len(x) == 0:
2396
+ return 0
2397
+ pca = PCA(n_components=2)
2398
+ pca.fit(np.vstack((x, y)).T)
2399
+ angle = np.arctan2(pca.components_[0, 1], pca.components_[0, 0]) * 180 / np.pi
2400
+ return angle
2401
+
2402
+ # Gradient Orientation-based angle detection
2403
+ elif "gra" in by:
2404
+ gx, gy = np.gradient(gray_image)
2405
+ angles = np.arctan2(gy, gx) * 180 / np.pi
2406
+ hist, bin_edges = np.histogram(angles, bins=360, range=(-180, 180))
2407
+ return bin_edges[np.argmax(hist)]
2408
+
2409
+ # Template Matching-based angle detection
2410
+ elif "temp" in by:
2411
+ if template is None:
2412
+ # Automatically extract a template from the center of the image
2413
+ height, width = gray_image.shape
2414
+ center_x, center_y = width // 2, height // 2
2415
+ size = (
2416
+ min(height, width) // 4
2417
+ ) # Size of the template as a fraction of image size
2418
+ template = gray_image[
2419
+ center_y - size : center_y + size, center_x - size : center_x + size
2420
+ ]
2421
+ best_angle = None
2422
+ best_corr = -1
2423
+ for angle in range(0, 180, 1): # Checking every degree
2424
+ rotated_template = transform.rotate(template, angle)
2425
+ res = cv2.matchTemplate(gray_image, rotated_template, cv2.TM_CCOEFF)
2426
+ _, max_val, _, _ = cv2.minMaxLoc(res)
2427
+ if max_val > best_corr:
2428
+ best_corr = max_val
2429
+ best_angle = angle
2430
+ return best_angle
2431
+
2432
+ # Image Moments-based angle detection
2433
+ elif "mo" in by:
2434
+ moments = measure.moments_central(gray_image)
2435
+ angle = (
2436
+ 0.5
2437
+ * np.arctan2(2 * moments[1, 1], moments[0, 2] - moments[2, 0])
2438
+ * 180
2439
+ / np.pi
2440
+ )
2441
+ return angle
2442
+
2443
+ # Fourier Transform-based angle detection
2444
+ elif "fft" in by:
2445
+ f = fft2(gray_image)
2446
+ fshift = fftshift(f)
2447
+ magnitude_spectrum = np.log(np.abs(fshift) + 1)
2448
+ rows, cols = magnitude_spectrum.shape
2449
+ r, c = np.unravel_index(np.argmax(magnitude_spectrum), (rows, cols))
2450
+ angle = np.arctan2(r - rows // 2, c - cols // 2) * 180 / np.pi
2451
+ return angle
2452
+
2453
+ else:
2454
+ print(f"Unknown method {by}")
2455
+ return 0
2287
2456
 
2288
2457
 
2289
2458
  def imgsets(img, **kwargs):
@@ -2444,7 +2613,11 @@ def imgsets(img, **kwargs):
2444
2613
  elif "cro" in k.lower() or "cut" in k.lower():
2445
2614
  img_update = img_update.crop(value)
2446
2615
  elif "rota" in k.lower():
2616
+ if isinstance(value, str):
2617
+ value = detect_angle(img_update, by=value)
2618
+ print(f"rotated by {value}°")
2447
2619
  img_update = img_update.rotate(value)
2620
+
2448
2621
  elif "si" in k.lower():
2449
2622
  img_update = img_update.resize(value)
2450
2623
  elif "thum" in k.lower():