py2ls 0.2.4.31__py3-none-any.whl → 0.2.4.33__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
py2ls/netfinder.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from bs4 import BeautifulSoup
2
+ import scrapy
2
3
  import requests
3
4
  import os
4
5
  import pandas as pd
@@ -332,6 +333,94 @@ def parse_cookies(cookies_str):
332
333
 
333
334
  return cookies_dict
334
335
 
336
+ class FetchSpider(scrapy.Spider):
337
+ name = "fetch_spider"
338
+
339
+ def __init__(self, url, parser="html.parser", cookies=None, headers=None, *args, **kwargs):
340
+ super(FetchSpider, self).__init__(*args, **kwargs)
341
+ self.start_urls = [url]
342
+ self.cookies = cookies
343
+ self.headers = headers
344
+ self.parser = parser
345
+
346
+ def start_requests(self):
347
+ for url in self.start_urls:
348
+ yield scrapy.Request(
349
+ url,
350
+ cookies=self.cookies,
351
+ headers=self.headers,
352
+ callback=self.parse
353
+ )
354
+
355
+ def parse(self, response):
356
+ # Use the desired parser (default: html.parser)
357
+ from bs4 import BeautifulSoup
358
+ soup = BeautifulSoup(response.text, self.parser)
359
+ yield {"content": soup}
360
+
361
+
362
+ def fetch_scrapy(
363
+ url,
364
+ parser="html.parser",
365
+ cookies=None,
366
+ headers=None,
367
+ settings=None,
368
+ ):
369
+ """
370
+ Fetches content using Scrapy.
371
+
372
+ Args:
373
+ url (str): The URL to scrape.
374
+ parser (str): Parser for BeautifulSoup (e.g., "lxml", "html.parser").
375
+ cookies (dict): Cookies to pass in the request.
376
+ headers (dict): HTTP headers for the request.
377
+ settings (dict): Scrapy settings, if any.
378
+
379
+ Returns:
380
+ dict: Parsed content as a dictionary.
381
+ """
382
+ from scrapy.utils.project import get_project_settings
383
+ from scrapy.crawler import CrawlerProcess
384
+ from scrapy.signalmanager import dispatcher
385
+ from scrapy import signals
386
+ import scrapy
387
+
388
+ # Container for scraped content
389
+ content = []
390
+
391
+ # Callback function for item scraped signal
392
+ def handle_item(item, response, spider):
393
+ content.append(item["content"])
394
+
395
+ # Scrapy settings
396
+ process_settings = settings or get_project_settings()
397
+ process_settings.update(
398
+ {
399
+ "USER_AGENT": "CustomUserAgent/1.0", # Use a custom user agent
400
+ "DOWNLOAD_DELAY": 1, # Prevent overloading servers
401
+ "COOKIES_ENABLED": bool(cookies),
402
+ "LOG_LEVEL": "ERROR", # Minimize log verbosity
403
+ }
404
+ )
405
+
406
+ # Initialize and configure Scrapy process
407
+ process = CrawlerProcess(settings=process_settings)
408
+ dispatcher.connect(handle_item, signal=signals.item_scraped)
409
+
410
+ # Start the Scrapy crawl
411
+ process.crawl(
412
+ FetchSpider,
413
+ url=url,
414
+ parser=parser,
415
+ cookies=cookies,
416
+ headers=headers,
417
+ )
418
+ process.start() # Blocks until all crawls are finished
419
+
420
+ # Return the first scraped content or None if empty
421
+ return content[0] if content else None
422
+
423
+
335
424
  def fetch_all(
336
425
  url,
337
426
  parser="lxml",
@@ -558,6 +647,16 @@ def fetch_all(
558
647
  else:
559
648
  logger.warning("Selenium could not fetch content")
560
649
  return None, None
650
+ elif 'scr' in driver.lower():
651
+ settings = {
652
+ "USER_AGENT": user_agent(),
653
+ "DOWNLOAD_DELAY": 1, # Prevent overloading the server
654
+ "COOKIES_ENABLED": True if cookies else False,
655
+ "LOG_LEVEL": "WARNING", # Reduce log verbosity
656
+ }
657
+ content=fetch_scrapy(url, parser=parser, cookies=cookies, headers=headers, settings=settings)
658
+ return parser, content
659
+
561
660
  except requests.RequestException as e:
562
661
  logger.error(f"Error fetching URL '{url}': {e}")
563
662
  return None, None
py2ls/ocr.py CHANGED
@@ -1,23 +1,16 @@
1
- import easyocr
1
+
2
2
  import cv2
3
3
  import numpy as np
4
4
  import matplotlib.pyplot as plt
5
5
  from py2ls.ips import (
6
6
  strcmp,
7
7
  detect_angle,
8
- ) # Ensure this function is defined in your 'ips' module
9
- from spellchecker import SpellChecker
10
- import re
11
-
12
- from PIL import Image, ImageDraw, ImageFont
13
- import PIL.PngImagePlugin
14
- import pytesseract
15
- from paddleocr import PaddleOCR
8
+ str2words,
9
+ isa
10
+ )
16
11
  import logging
17
-
18
- logging.getLogger("ppocr").setLevel(
19
- logging.WARNING
20
- ) # or logging.ERROR to show only error messages
12
+ #logging.getLogger("ppocr").setLevel(logging.ERROR)
13
+ logging.getLogger("ppocr").setLevel(logging.WARNING)
21
14
 
22
15
  """
23
16
  Optical Character Recognition (OCR)
@@ -285,10 +278,12 @@ def add_text_pil(
285
278
  image,
286
279
  text,
287
280
  position,
281
+ cvt_cmp=True,
288
282
  font_size=12,
289
283
  color=(0, 0, 0),
290
284
  bg_color=(133, 203, 245, 100),
291
285
  ):
286
+ from PIL import Image, ImageDraw, ImageFont
292
287
  # Convert the image to PIL format
293
288
  pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)).convert("RGBA")
294
289
  # Define the font (make sure to use a font that supports Chinese characters)
@@ -337,7 +332,7 @@ def add_text_pil(
337
332
  overlay = overlay.convert("RGBA")
338
333
  combined = Image.alpha_composite(pil_image, overlay)
339
334
  # Convert the image back to OpenCV format
340
- image = cv2.cvtColor(np.array(combined), cv2.COLOR_RGBA2BGR)
335
+ image = cv2.cvtColor(np.array(combined), cv2.COLOR_RGBA2BGR) #if cvt_cmp else np.array(combined)
341
336
  return image
342
337
 
343
338
 
@@ -348,7 +343,7 @@ def preprocess_img(
348
343
  threshold_method="adaptive",
349
344
  rotate="auto",
350
345
  skew=False,
351
- blur=True,
346
+ blur=False,#True,
352
347
  blur_ksize=(5, 5),
353
348
  morph=True,
354
349
  morph_op="open",
@@ -384,12 +379,14 @@ def preprocess_img(
384
379
  clahe_grid_size: CLAHE 的网格大小。
385
380
  edge_detection: 是否进行边缘检测。
386
381
  """
382
+ import PIL.PngImagePlugin
387
383
  if isinstance(image, PIL.PngImagePlugin.PngImageFile):
388
384
  image = np.array(image)
389
385
  if isinstance(image, str):
390
386
  image = cv2.imread(image)
391
387
  if not isinstance(image, np.ndarray):
392
388
  image = np.array(image)
389
+
393
390
  try:
394
391
  if image.shape[1] == 4: # Check if it has an alpha channel
395
392
  # Drop the alpha channel (if needed), or handle it as required
@@ -507,6 +504,8 @@ def text_postprocess(
507
504
  pattern=None,
508
505
  merge=True,
509
506
  ):
507
+ import re
508
+ from spellchecker import SpellChecker
510
509
 
511
510
  def correct_spelling(text_list):
512
511
  spell = SpellChecker()
@@ -531,9 +530,9 @@ def text_postprocess(
531
530
  return merged_text
532
531
 
533
532
  results = text
534
- print(results)
535
533
  if spell_check:
536
- results = correct_spelling(results)
534
+ # results = correct_spelling(results)
535
+ results=str2words(results)
537
536
  if clean:
538
537
  results = clean_text(results)
539
538
  if filter:
@@ -552,42 +551,39 @@ def get_text(
552
551
  image,
553
552
  lang=["ch_sim", "en"],
554
553
  model="paddleocr", # "pytesseract","paddleocr","easyocr"
555
- thr=0.1,
554
+ thr=0.1,
556
555
  gpu=True,
557
556
  decoder="wordbeamsearch", #'greedy', 'beamsearch' and 'wordbeamsearch'(hightly accurate)
558
557
  output="txt",
559
558
  preprocess=None,
560
- postprocess="not ready",
559
+ postprocess=False,# do not check spell
561
560
  show=True,
562
561
  ax=None,
563
562
  cmap=cv2.COLOR_BGR2RGB, # draw_box
564
- font=cv2.FONT_HERSHEY_SIMPLEX,
565
- font_scale=0.8,
566
- thickness_text=2, # Line thickness of 2 px
567
- box_color=(0, 255, 0), # draw_box
568
- font_color=(0, 0, 0),
569
- bg_color=(133, 203, 245, 100),
563
+ font=cv2.FONT_HERSHEY_SIMPLEX,# draw_box
564
+ fontsize=8,# draw_box
565
+ figsize=[10,10],
566
+ box_color = (0, 255, 0), # draw_box
567
+ fontcolor = (0, 0, 0),# draw_box
568
+ bg_color=(133, 203, 245, 100),# draw_box
570
569
  usage=False,
571
570
  **kwargs,
572
571
  ):
573
572
  """
574
- 功能: 该函数使用 EasyOCR 进行文本识别,并允许自定义图像预处理步骤和结果展示。
575
- 参数:
576
- image: 输入的图像路径或图像数据。
577
- lang: OCR 语言列表。
578
- thr: 置信度阈值,低于此阈值的检测结果将被过滤。
579
- gpu: 是否使用 GPU。
580
- output: 输出类型,可以是 'all'(返回所有检测结果)、'text'(返回文本)、'score'(返回置信度分数)、'box'(返回边界框)。
581
- preprocess: 预处理参数字典,传递给 preprocess_img 函数。
582
- show: 是否显示结果图像。
583
- ax: 用于显示图像的 Matplotlib 子图。
584
- cmap: 用于显示图像的颜色映射。
585
- box_color: 边界框的颜色。
586
- font_color: 文本的颜色。
587
- kwargs: 传递给 EasyOCR readtext 函数的其他参数。
588
-
589
- # Uage
573
+ image: 输入的图像路径或图像数据。
574
+ lang: OCR 语言列表。
575
+ thr: 置信度阈值,低于此阈值的检测结果将被过滤。
576
+ gpu: 是否使用 GPU。
577
+ output: 输出类型,可以是 'all'(返回所有检测结果)、'text'(返回文本)、'score'(返回置信度分数)、'box'(返回边界框)。
578
+ preprocess: 预处理参数字典,传递给 preprocess_img 函数。
579
+ show: 是否显示结果图像。
580
+ ax: 用于显示图像的 Matplotlib 子图。
581
+ cmap: 用于显示图像的颜色映射。
582
+ box_color: 边界框的颜色。
583
+ fontcolor: 文本的颜色。
584
+ kwargs: 传递给 EasyOCR readtext 函数的其他参数。
590
585
  """
586
+ from PIL import Image
591
587
  if usage:
592
588
  print(
593
589
  """
@@ -612,16 +608,20 @@ def get_text(
612
608
  "edge_detection": False
613
609
  },
614
610
  adjust_contrast=0.7
615
- )
616
- """
617
- )
611
+ )""")
618
612
 
619
- models = ["easyocr", "paddleocr", "pytesseract","ddddocr"]
613
+ models = ["easyocr", "paddleocr", "pytesseract","ddddocr","zerox"]
620
614
  model = strcmp(model, models)[0]
621
615
  lang = lang_auto_detect(lang, model)
622
- if isinstance(image, str):
623
- dir_img=image
616
+ cvt_cmp=True
617
+ if isinstance(image, str) and isa(image,'file'):
624
618
  image = cv2.imread(image)
619
+ elif isa(image,'image'):
620
+ cvt_cmp=False
621
+ print(1)
622
+ image = np.array(image)
623
+ else:
624
+ raise ValueError(f"not support image with {type(image)} type")
625
625
 
626
626
  # Ensure lang is always a list
627
627
  if isinstance(lang, str):
@@ -631,110 +631,94 @@ def get_text(
631
631
  if preprocess is None:
632
632
  preprocess = {}
633
633
  image_process = preprocess_img(image, **preprocess)
634
+ plt.figure(figsize=figsize) if show else None
635
+ # plt.subplot(131)
636
+ # plt.imshow(cv2.cvtColor(image, cmap)) if cvt_cmp else plt.imshow(image)
637
+ # plt.subplot(132)
638
+ # plt.imshow(image_process)
639
+ # plt.subplot(133)
634
640
  if "easy" in model.lower():
641
+ import easyocr
635
642
  print(f"detecting language(s):{lang}")
636
643
  # Perform OCR on the image
637
644
  reader = easyocr.Reader(lang, gpu=gpu)
638
645
  detections = reader.readtext(image_process, decoder=decoder, **kwargs)
639
- if postprocess is None:
640
- postprocess = dict(
641
- spell_check=True,
642
- clean=True,
643
- filter=dict(min_length=2),
644
- pattern=None,
645
- merge=True,
646
- )
647
- text_corr = []
648
- [
649
- text_corr.extend(text_postprocess(text, **postprocess))
650
- for _, text, _ in detections
651
- ]
646
+
647
+ text_corr = []
648
+ for _, text, _ in detections:
649
+ text_corr.append(text_postprocess(text) if postprocess else text)
650
+
652
651
  if show:
653
652
  if ax is None:
654
653
  ax = plt.gca()
655
- for bbox, text, score in detections:
654
+ for i, (bbox, text, score) in enumerate(detections):
656
655
  if score > thr:
657
656
  top_left = tuple(map(int, bbox[0]))
658
657
  bottom_right = tuple(map(int, bbox[2]))
659
- image = cv2.rectangle(image, top_left, bottom_right, box_color, 2)
660
- # image = cv2.putText(
661
- # image, text, top_left, font, font_scale, font_color, thickness_text
662
- # )
658
+ image = cv2.rectangle(image, top_left, bottom_right, box_color, 2)
663
659
  image = add_text_pil(
664
660
  image,
665
- text,
661
+ text_corr[i],
666
662
  top_left,
667
- font_size=font_scale * 32,
668
- color=font_color,
663
+ cvt_cmp=cvt_cmp,
664
+ font_size=fontsize *6,
665
+ color=fontcolor,
669
666
  )
670
- # img_cmp = cv2.cvtColor(image, cmap)
671
- ax.imshow(image)
667
+ try:
668
+ img_cmp = cv2.cvtColor(image, cmap) if cvt_cmp else image
669
+ except:
670
+ img_cmp=image
671
+
672
+ ax.imshow(img_cmp) if cvt_cmp else ax.imshow(image)
672
673
  ax.axis("off")
673
- # plt.show()
674
- # 根据输出类型返回相应的结果
674
+
675
675
  if output == "all":
676
676
  return ax, detections
677
677
  elif "t" in output.lower() and "x" in output.lower():
678
- # 提取文本,过滤低置信度的结果
679
678
  text = [text_ for _, text_, score_ in detections if score_ >= thr]
680
679
  if postprocess:
681
680
  return ax, text
682
681
  else:
683
682
  return text_corr
684
683
  elif "score" in output.lower() or "prob" in output.lower():
685
- # 提取分数
686
684
  scores = [score_ for _, _, score_ in detections]
687
685
  return ax, scores
688
686
  elif "box" in output.lower():
689
- # 提取边界框,过滤低置信度的结果
690
687
  bboxes = [bbox_ for bbox_, _, score_ in detections if score_ >= thr]
691
688
  return ax, bboxes
692
689
  else:
693
- # 默认返回所有检测信息
694
690
  return ax, detections
695
691
  else:
696
- # 根据输出类型返回相应的结果
697
692
  if output == "all":
698
693
  return detections
699
694
  elif "t" in output.lower() and "x" in output.lower():
700
- # 提取文本,过滤低置信度的结果
701
695
  text = [text_ for _, text_, score_ in detections if score_ >= thr]
702
696
  return text
703
697
  elif "score" in output.lower() or "prob" in output.lower():
704
- # 提取分数
705
698
  scores = [score_ for _, _, score_ in detections]
706
699
  return scores
707
700
  elif "box" in output.lower():
708
- # 提取边界框,过滤低置信度的结果
709
701
  bboxes = [bbox_ for bbox_, _, score_ in detections if score_ >= thr]
710
702
  return bboxes
711
703
  else:
712
- # 默认返回所有检测信息
713
704
  return detections
714
705
  elif "pad" in model.lower():
706
+ from paddleocr import PaddleOCR
707
+ lang=strcmp(lang, ['ch','en','french','german','korean','japan'])[0]
715
708
  ocr = PaddleOCR(
716
709
  use_angle_cls=True,
717
710
  cls=True,
711
+ lang=lang
718
712
  ) # PaddleOCR supports only one language at a time
719
- result = ocr.ocr(image_process, **kwargs)
713
+ cls=kwargs.pop('cls',True)
714
+ result = ocr.ocr(image_process,cls=cls, **kwargs)
720
715
  detections = []
721
716
  if result[0] is not None:
722
717
  for line in result[0]:
723
718
  bbox, (text, score) = line
719
+ text = str2words(text) if postprocess else text # check spell
724
720
  detections.append((bbox, text, score))
725
- if postprocess is None:
726
- postprocess = dict(
727
- spell_check=True,
728
- clean=True,
729
- filter=dict(min_length=2),
730
- pattern=None,
731
- merge=True,
732
- )
733
- text_corr = []
734
- [
735
- text_corr.extend(text_postprocess(text, **postprocess))
736
- for _, text, _ in detections
737
- ]
721
+
738
722
  if show:
739
723
  if ax is None:
740
724
  ax = plt.gca()
@@ -746,60 +730,48 @@ def get_text(
746
730
  ) # Bottom-left for more accurate placement
747
731
  bottom_right = tuple(map(int, bbox[2]))
748
732
  image = cv2.rectangle(image, top_left, bottom_right, box_color, 2)
749
- # image = cv2.putText(
750
- # image, text, top_left, font, font_scale, font_color, thickness_text
751
- # )
752
733
  image = add_text_pil(
753
734
  image,
754
735
  text,
755
736
  top_left,
756
- font_size=font_scale * 32,
757
- color=font_color,
737
+ cvt_cmp=cvt_cmp,
738
+ font_size=fontsize *6,
739
+ color=fontcolor,
758
740
  bg_color=bg_color,
759
741
  )
760
- img_cmp = cv2.cvtColor(image, cmap)
761
- ax.imshow(image)
742
+ try:
743
+ img_cmp = cv2.cvtColor(image, cmap) if cvt_cmp else image
744
+ except:
745
+ img_cmp = image
746
+
747
+ ax.imshow(img_cmp)
762
748
  ax.axis("off")
763
- # plt.show()
764
- # 根据输出类型返回相应的结果
765
749
  if output == "all":
766
750
  return ax, detections
767
751
  elif "t" in output.lower() and "x" in output.lower():
768
- # 提取文本,过滤低置信度的结果
769
752
  text = [text_ for _, text_, score_ in detections if score_ >= thr]
770
- if postprocess:
771
- return ax, text
772
- else:
773
- return text_corr
753
+ return ax, text
774
754
  elif "score" in output.lower() or "prob" in output.lower():
775
- # 提取分数
776
755
  scores = [score_ for _, _, score_ in detections]
777
756
  return ax, scores
778
757
  elif "box" in output.lower():
779
- # 提取边界框,过滤低置信度的结果
780
758
  bboxes = [bbox_ for bbox_, _, score_ in detections if score_ >= thr]
781
759
  return ax, bboxes
782
760
  else:
783
- # 默认返回所有检测信息
784
761
  return ax, detections
785
762
  else:
786
- # 根据输出类型返回相应的结果
787
763
  if output == "all":
788
764
  return detections
789
765
  elif "t" in output.lower() and "x" in output.lower():
790
- # 提取文本,过滤低置信度的结果
791
766
  text = [text_ for _, text_, score_ in detections if score_ >= thr]
792
767
  return text
793
768
  elif "score" in output.lower() or "prob" in output.lower():
794
- # 提取分数
795
769
  scores = [score_ for _, _, score_ in detections]
796
770
  return scores
797
771
  elif "box" in output.lower():
798
- # 提取边界框,过滤低置信度的结果
799
772
  bboxes = [bbox_ for bbox_, _, score_ in detections if score_ >= thr]
800
773
  return bboxes
801
774
  else:
802
- # 默认返回所有检测信息
803
775
  return detections
804
776
  elif "ddddocr" in model.lower():
805
777
  import ddddocr
@@ -844,7 +816,51 @@ def get_text(
844
816
  ax.imshow(image_vis)
845
817
  ax.axis("off")
846
818
  return detections
819
+
820
+ elif "zerox" in model.lower():
821
+ from pyzerox import zerox
822
+ result = zerox(image_process)
823
+ detections = [(bbox, text, score) for bbox, text, score in result]
824
+ # Postprocess and visualize
825
+ if postprocess is None:
826
+ postprocess = dict(
827
+ spell_check=True,
828
+ clean=True,
829
+ filter=dict(min_length=2),
830
+ pattern=None,
831
+ merge=True,
832
+ )
833
+ text_corr = [text_postprocess(text, **postprocess) for _, text, _ in detections]
834
+
835
+ # Display results if 'show' is True
836
+ if show:
837
+ if ax is None:
838
+ ax = plt.gca()
839
+ for bbox, text, score in detections:
840
+ if score > thr:
841
+ top_left = tuple(map(int, bbox[0]))
842
+ bottom_right = tuple(map(int, bbox[2]))
843
+ image = cv2.rectangle(image, top_left, bottom_right, box_color, 2)
844
+ image = add_text_pil(image, text, top_left, cvt_cmp=cvt_cmp,font_size=fontsize *6, color=fontcolor, bg_color=bg_color)
845
+ ax.imshow(image)
846
+ ax.axis("off")
847
+
848
+ # Return result based on 'output' type
849
+ if output == "all":
850
+ return ax, detections
851
+ elif "t" in output.lower() and "x" in output.lower():
852
+ text = [text_ for _, text_, score_ in detections if score_ >= thr]
853
+ return ax, text
854
+ elif "score" in output.lower() or "prob" in output.lower():
855
+ scores = [score_ for _, _, score_ in detections]
856
+ return ax, scores
857
+ elif "box" in output.lower():
858
+ bboxes = [bbox_ for bbox_, _, score_ in detections if score_ >= thr]
859
+ return ax, bboxes
860
+ else:
861
+ return detections
847
862
  else: # "pytesseract"
863
+ import pytesseract
848
864
  if ax is None:
849
865
  ax = plt.gca()
850
866
  text = pytesseract.image_to_string(image_process, lang="+".join(lang), **kwargs)
@@ -869,8 +885,9 @@ def get_text(
869
885
  image,
870
886
  char,
871
887
  left,
872
- font_size=font_scale * 32,
873
- color=font_color,
888
+ cvt_cmp=cvt_cmp,
889
+ font_size=fontsize *6,
890
+ color=fontcolor,
874
891
  )
875
892
  img_cmp = cv2.cvtColor(image, cmap)
876
893
  ax.imshow(img_cmp)
@@ -906,8 +923,8 @@ def draw_box(
906
923
  thr=0.25,
907
924
  cmap=cv2.COLOR_BGR2RGB,
908
925
  box_color=(0, 255, 0), # draw_box
909
- font_color=(0, 0, 255), # draw_box
910
- font_scale=0.8,
926
+ fontcolor=(0, 0, 255), # draw_box
927
+ fontsize=8,
911
928
  show=True,
912
929
  ax=None,
913
930
  **kwargs,
@@ -924,12 +941,9 @@ def draw_box(
924
941
  if score > thr:
925
942
  top_left = tuple(map(int, bbox[0]))
926
943
  bottom_right = tuple(map(int, bbox[2]))
927
- image = cv2.rectangle(image, top_left, bottom_right, box_color, 2)
928
- # image = cv2.putText(
929
- # image, text, top_left, font, font_scale, font_color, thickness_text
930
- # )
944
+ image = cv2.rectangle(image, top_left, bottom_right, box_color, 2)
931
945
  image = add_text_pil(
932
- image, text, top_left, font_size=font_scale * 32, color=font_color
946
+ image, text, top_left, cvt_cmp=cvt_cmp,font_size=fontsize *6, color=fontcolor
933
947
  )
934
948
 
935
949
  img_cmp = cv2.cvtColor(image, cmap)