jebin2 commited on
Commit
7f3435a
·
1 Parent(s): ef8b3f1

added dial rem

Browse files
comic_panel_extractor/border_panel_extractor.py CHANGED
@@ -138,14 +138,16 @@ class BorderPanelExtractor:
138
  # Draw bounding rectangles
139
  img_h, img_w = original_image.shape[:2]
140
  image_area = img_h * img_w
141
- max_ratio = 0.7 # Max box area must be less than 70% of image
142
  for cnt in contours:
143
  x, y, w, h = cv2.boundingRect(cnt)
144
  box_area = w * h
145
- if box_area / image_area < max_ratio:
146
- minc, minr = x, y
147
- maxc, maxr = x + w, y + h
148
- accepted_boxes.append((minc, minr, maxc, maxr))
 
 
 
149
 
150
  orig_pil = Image.fromarray(original_image)
151
  self._create_visualization(orig_pil, accepted_boxes, "extract_with_contours.jpg")
@@ -311,24 +313,33 @@ class BorderPanelExtractor:
311
 
312
  return all_paths, output_path
313
 
314
- def draw_black(self, original_image, accepted_boxes) -> None:
315
  orig_pil = Image.fromarray(original_image.copy())
316
- draw = ImageDraw.Draw(orig_pil)
317
 
 
 
 
318
  stripe_height = 10
319
 
 
 
 
 
 
 
 
320
  for x1, y1, x2, y2 in accepted_boxes:
321
- for y in range(y1, y2, stripe_height):
322
- color = (0, 0, 0) if ((y - y1) // stripe_height) % 2 == 0 else (255, 255, 255)
323
- y_end = min(y + stripe_height, y2)
324
- draw.rectangle([x1, y, x2, y_end], fill=color)
325
-
326
- # Save the result
327
  output_path = os.path.join(self.config.output_folder, "00_original_with_panels_removed.jpg")
328
  orig_pil.save(output_path)
329
-
330
  return output_path
331
 
 
332
  def get_black_white_ratio(self, image_path: str, threshold: int = 128) -> dict:
333
  """
334
  Calculate the ratio of black and white pixels in a binary image.
@@ -518,83 +529,85 @@ class BorderPanelExtractor:
518
 
519
  def extend_to_nearby_boxes(self, boxes, image_shape):
520
  """
521
- Extend smaller boxes to the edge of close larger boxes, without merging or reducing the box count.
 
522
 
523
  A box is represented by (x1, y1, x2, y2).
524
  """
525
  if not boxes:
526
  return boxes
527
- extended_boxes = [list(box) for box in boxes]
528
- height, width = image_shape[:2]
529
-
530
- width_threshold = min(x2 - x1 for x1, y1, x2, y2 in extended_boxes)
531
- height_threshold = min(y2 - y1 for x1, y1, x2, y2 in extended_boxes)
532
 
533
- # width_threshold = self.config.min_width_ratio * width
534
- # height_threshold = self.config.min_height_ratio * height
535
-
536
- # print(f"[DEBUG] Image Shape: {image_shape}, Width Threshold: {width_threshold:.2f}, Height Threshold: {height_threshold:.2f}\n")
537
-
538
- for i in range(len(extended_boxes)):
539
- for j in range(len(extended_boxes)):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
540
  if i == j:
541
  continue
542
 
543
- box1 = extended_boxes[i]
544
- box2 = extended_boxes[j]
545
-
546
- area1 = (box1[2] - box1[0]) * (box1[3] - box1[1])
547
- area2 = (box2[2] - box2[0]) * (box2[3] - box2[1])
548
-
549
- if area1 >= area2:
550
- continue
551
-
552
- # print(f"[DEBUG] Comparing smaller Box {i} {box1} with larger Box {j} {box2}")
553
-
554
- x1_1, y1_1, x2_1, y2_1 = box1
555
- x1_2, y1_2, x2_2, y2_2 = box2
556
-
557
- # Horizontal Extension Check
558
- is_vertically_aligned = (y1_1 < y2_2 and y2_1 > y1_2)
559
- if is_vertically_aligned:
560
- gap_right = x1_2 - x2_1
561
- if 0 < gap_right <= width_threshold:
562
- # print(f" [INFO] Extending right of Box {i}. Gap ({gap_right:.2f}) <= Threshold ({width_threshold:.2f})")
563
- extended_boxes[i][2] = x1_2
564
- # elif gap_right > width_threshold:
565
- # print(f" [DEBUG] Did not extend right: Gap ({gap_right:.2f}) > Threshold ({width_threshold:.2f})")
566
-
567
- gap_left = x1_1 - x2_2
568
- if 0 < gap_left <= width_threshold:
569
- # print(f" [INFO] Extending left of Box {i}. Gap ({gap_left:.2f}) <= Threshold ({width_threshold:.2f})")
570
- extended_boxes[i][0] = x2_2
571
- # elif gap_left > width_threshold:
572
- # print(f" [DEBUG] Did not extend left: Gap ({gap_left:.2f}) > Threshold ({width_threshold:.2f})")
573
- # else:
574
- # print(f" [DEBUG] Not vertically aligned for horizontal extension.")
575
-
576
-
577
- # Vertical Extension Check
578
- is_horizontally_aligned = (x1_1 < x2_2 and x2_1 > x1_2)
579
- if is_horizontally_aligned:
580
- gap_bottom = y1_2 - y2_1
581
- if 0 < gap_bottom <= height_threshold:
582
- # print(f" [INFO] Extending bottom of Box {i}. Gap ({gap_bottom:.2f}) <= Threshold ({height_threshold:.2f})")
583
- extended_boxes[i][3] = y1_2
584
- # elif gap_bottom > height_threshold:
585
- # print(f" [DEBUG] Did not extend bottom: Gap ({gap_bottom:.2f}) > Threshold ({height_threshold:.2f})")
586
-
587
- gap_top = y1_1 - y2_2
588
- if 0 < gap_top <= height_threshold:
589
- # print(f" [INFO] Extending top of Box {i}. Gap ({gap_top:.2f}) <= Threshold ({height_threshold:.2f})")
590
- extended_boxes[i][1] = y2_2
591
- # elif gap_top > height_threshold:
592
- # print(f" [DEBUG] Did not extend top: Gap ({gap_top:.2f}) > Threshold ({height_threshold:.2f})")
593
- # else:
594
- # print(f" [DEBUG] Not horizontally aligned for vertical extension.")
595
- # print("-" * 20)
596
-
597
- return [tuple(box) for box in extended_boxes]
598
 
599
  def threshold_based_filter(self, boxes, image_shape):
600
  img_h, img_w = image_shape[:2]
@@ -615,20 +628,32 @@ class BorderPanelExtractor:
615
  def _apply_additional_processing(self, segmentation_mask_path: str) -> np.ndarray:
616
  """Apply additional image processing steps when needed."""
617
  image_processor = ImageProcessor()
 
 
 
 
 
 
 
 
 
 
 
 
618
 
619
  # Step 5: Thicken black lines
620
  processed_path = image_processor.thick_black(
621
- segmentation_mask_path,
622
- file_name="04_thick.jpg",
623
  output_folder=f"{self.output_folder}"
624
  )
625
 
626
  # Step 6: Connect gaps
627
- processed_path = image_processor.connect_horizontal_vertical_gaps(
628
- processed_path,
629
- file_name="05_continuity.jpg",
630
- output_folder=f"{self.output_folder}"
631
- )
632
 
633
  # Check if more processing is needed
634
  pixel_ratios = self.get_black_white_ratio(processed_path)
@@ -636,19 +661,19 @@ class BorderPanelExtractor:
636
  # Additional processing steps
637
  processed_path = image_processor.thin_image_borders(
638
  processed_path,
639
- file_name="06_thin.jpg",
640
  output_folder=f"{self.output_folder}"
641
  )
642
 
643
  processed_path = image_processor.remove_dangling_lines(
644
  processed_path,
645
- file_name="07_remove_dangling_lines.jpg",
646
  output_folder=f"{self.output_folder}"
647
  )
648
 
649
  processed_path = image_processor.thick_black(
650
  processed_path,
651
- file_name="08_thick.jpg",
652
  output_folder=f"{self.output_folder}"
653
  )
654
 
 
138
  # Draw bounding rectangles
139
  img_h, img_w = original_image.shape[:2]
140
  image_area = img_h * img_w
 
141
  for cnt in contours:
142
  x, y, w, h = cv2.boundingRect(cnt)
143
  box_area = w * h
144
+ # Check size thresholds
145
+ if box_area > image_area * 0.8 or not self._meets_size_requirements(box_area, w, h, image_area, img_w, img_h):
146
+ continue
147
+
148
+ minc, minr = x, y
149
+ maxc, maxr = x + w, y + h
150
+ accepted_boxes.append((minc, minr, maxc, maxr))
151
 
152
  orig_pil = Image.fromarray(original_image)
153
  self._create_visualization(orig_pil, accepted_boxes, "extract_with_contours.jpg")
 
313
 
314
  return all_paths, output_path
315
 
316
+ def draw_black(self, original_image, accepted_boxes) -> str:
317
  orig_pil = Image.fromarray(original_image.copy())
318
+ width, height = orig_pil.size
319
 
320
+ # Create a global stripe pattern (black and white horizontal stripes)
321
+ stripe_img = Image.new("RGB", (width, height), (255, 255, 255))
322
+ draw = ImageDraw.Draw(stripe_img)
323
  stripe_height = 10
324
 
325
+ for y in range(0, height, stripe_height):
326
+ if (y // stripe_height) % 2 == 0:
327
+ draw.rectangle([0, y, width, min(y + stripe_height, height)], fill=(0, 0, 0))
328
+
329
+ # Create a mask where accepted boxes will be applied
330
+ mask = Image.new("L", (width, height), 0)
331
+ mask_draw = ImageDraw.Draw(mask)
332
  for x1, y1, x2, y2 in accepted_boxes:
333
+ mask_draw.rectangle([x1, y1, x2, y2], fill=255)
334
+
335
+ # Paste the striped image only where mask is white (inside accepted boxes)
336
+ orig_pil.paste(stripe_img, (0, 0), mask)
337
+
 
338
  output_path = os.path.join(self.config.output_folder, "00_original_with_panels_removed.jpg")
339
  orig_pil.save(output_path)
 
340
  return output_path
341
 
342
+
343
  def get_black_white_ratio(self, image_path: str, threshold: int = 128) -> dict:
344
  """
345
  Calculate the ratio of black and white pixels in a binary image.
 
529
 
530
  def extend_to_nearby_boxes(self, boxes, image_shape):
531
  """
532
+ Extends boxes to the edge of any close neighboring box without causing
533
+ unintended merging by using an atomic update approach.
534
 
535
  A box is represented by (x1, y1, x2, y2).
536
  """
537
  if not boxes:
538
  return boxes
 
 
 
 
 
539
 
540
+ height, width = image_shape[:2]
541
+ # Ensure you have self.config.min_width_ratio and self.config.min_height_ratio defined
542
+ # For example:
543
+ # self.config.min_width_ratio = 0.05
544
+ # self.config.min_height_ratio = 0.05
545
+ width_threshold = width * self.config.min_width_ratio
546
+ height_threshold = height * self.config.min_height_ratio
547
+
548
+ final_boxes = []
549
+ # For each box, calculate its new coordinates based on the original list
550
+ for i in range(len(boxes)):
551
+ # Start with the original coordinates for the box we're currently processing
552
+ x1, y1, x2, y2 = boxes[i]
553
+
554
+ # These will store the closest boundaries we can extend to,
555
+ # initialized to the image edges.
556
+ closest_left_boundary = 0
557
+ closest_right_boundary = width
558
+ closest_top_boundary = 0
559
+ closest_bottom_boundary = height
560
+
561
+ # Find the closest neighbor on each of the four sides by checking against ALL other boxes
562
+ for j in range(len(boxes)):
563
  if i == j:
564
  continue
565
 
566
+ x1_j, y1_j, x2_j, y2_j = boxes[j]
567
+
568
+ # Check for neighbors to the RIGHT of box `i`
569
+ is_vert_overlap = (y1 < y2_j and y2 > y1_j) # Do they overlap vertically?
570
+ is_right_neighbor = (x1_j >= x2) # Is box `j` to the right of `i`?
571
+ if is_vert_overlap and is_right_neighbor:
572
+ closest_right_boundary = min(closest_right_boundary, x1_j)
573
+
574
+ # Check for neighbors to the LEFT of box `i`
575
+ is_left_neighbor = (x2_j <= x1) # Is box `j` to the left of `i`?
576
+ if is_vert_overlap and is_left_neighbor:
577
+ closest_left_boundary = max(closest_left_boundary, x2_j)
578
+
579
+ # Check for neighbors BELOW box `i`
580
+ is_horiz_overlap = (x1 < x2_j and x2 > x1_j) # Do they overlap horizontally?
581
+ is_bottom_neighbor = (y1_j >= y2) # Is box `j` below `i`?
582
+ if is_horiz_overlap and is_bottom_neighbor:
583
+ closest_bottom_boundary = min(closest_bottom_boundary, y1_j)
584
+
585
+ # Check for neighbors ABOVE box `i`
586
+ is_top_neighbor = (y2_j <= y1) # Is box `j` above `i`?
587
+ if is_horiz_overlap and is_top_neighbor:
588
+ closest_top_boundary = max(closest_top_boundary, y2_j)
589
+
590
+ # --- Apply the calculated extensions ---
591
+
592
+ # Extend right if the closest gap on the right is within the threshold
593
+ if 0 < (closest_right_boundary - x2) <= width_threshold:
594
+ x2 = closest_right_boundary
595
+
596
+ # Extend left
597
+ if 0 < (x1 - closest_left_boundary) <= width_threshold:
598
+ x1 = closest_left_boundary
599
+
600
+ # Extend down
601
+ if 0 < (closest_bottom_boundary - y2) <= height_threshold:
602
+ y2 = closest_bottom_boundary
603
+
604
+ # Extend up
605
+ if 0 < (y1 - closest_top_boundary) <= height_threshold:
606
+ y1 = closest_top_boundary
607
+
608
+ final_boxes.append(tuple(map(int, (x1, y1, x2, y2))))
609
+
610
+ return final_boxes
 
 
 
 
 
 
 
 
 
 
611
 
612
  def threshold_based_filter(self, boxes, image_shape):
613
  img_h, img_w = image_shape[:2]
 
628
  def _apply_additional_processing(self, segmentation_mask_path: str) -> np.ndarray:
629
  """Apply additional image processing steps when needed."""
630
  image_processor = ImageProcessor()
631
+
632
+ processed_path = image_processor.remove_diagonal_lines_and_set_white(segmentation_mask_path,
633
+ file_name="04_remove_diagonal_lines_and_set_white.jpg",
634
+ output_folder=f"{self.output_folder}")
635
+
636
+ processed_path = image_processor.remove_dangling_lines(processed_path,
637
+ file_name="05_remove_dangling_lines.jpg",
638
+ output_folder=f"{self.output_folder}")
639
+
640
+ processed_path = image_processor.remove_diagonal_only_cells(processed_path,
641
+ file_name="06_remove_diagonal_only_cells.jpg",
642
+ output_folder=f"{self.output_folder}")
643
 
644
  # Step 5: Thicken black lines
645
  processed_path = image_processor.thick_black(
646
+ processed_path,
647
+ file_name="07_thick.jpg",
648
  output_folder=f"{self.output_folder}"
649
  )
650
 
651
  # Step 6: Connect gaps
652
+ # processed_path = image_processor.connect_horizontal_vertical_gaps(
653
+ # processed_path,
654
+ # file_name="05_continuity.jpg",
655
+ # output_folder=f"{self.output_folder}"
656
+ # )
657
 
658
  # Check if more processing is needed
659
  pixel_ratios = self.get_black_white_ratio(processed_path)
 
661
  # Additional processing steps
662
  processed_path = image_processor.thin_image_borders(
663
  processed_path,
664
+ file_name="08_thin.jpg",
665
  output_folder=f"{self.output_folder}"
666
  )
667
 
668
  processed_path = image_processor.remove_dangling_lines(
669
  processed_path,
670
+ file_name="09_remove_dangling_lines.jpg",
671
  output_folder=f"{self.output_folder}"
672
  )
673
 
674
  processed_path = image_processor.thick_black(
675
  processed_path,
676
+ file_name="010_thick.jpg",
677
  output_folder=f"{self.output_folder}"
678
  )
679