Add Fast Conditional-DETR Processor (#37071)
* Add Fast Conditional-DETR Processor * Update image_processing_conditional_detr_fast.py * Add modular_conditional_detr.py * Update image_processing_conditional_detr_fast.py * Update tests * make fix --------- Co-authored-by: Yoni Gozlan <74535834+yonigozlan@users.noreply.github.com>
This commit is contained in:
@@ -48,6 +48,11 @@ This model was contributed by [DepuMeng](https://huggingface.co/DepuMeng). The o
|
|||||||
|
|
||||||
[[autodoc]] ConditionalDetrImageProcessor
|
[[autodoc]] ConditionalDetrImageProcessor
|
||||||
- preprocess
|
- preprocess
|
||||||
|
|
||||||
|
## ConditionalDetrImageProcessorFast
|
||||||
|
|
||||||
|
[[autodoc]] ConditionalDetrImageProcessorFast
|
||||||
|
- preprocess
|
||||||
- post_process_object_detection
|
- post_process_object_detection
|
||||||
- post_process_instance_segmentation
|
- post_process_instance_segmentation
|
||||||
- post_process_semantic_segmentation
|
- post_process_semantic_segmentation
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ alt="描画" width="600"/>
|
|||||||
|
|
||||||
[[autodoc]] ConditionalDetrImageProcessor
|
[[autodoc]] ConditionalDetrImageProcessor
|
||||||
- preprocess
|
- preprocess
|
||||||
|
|
||||||
|
## ConditionalDetrImageProcessorFast
|
||||||
|
|
||||||
|
[[autodoc]] ConditionalDetrImageProcessorFast
|
||||||
|
- preprocess
|
||||||
- post_process_object_detection
|
- post_process_object_detection
|
||||||
- post_process_instance_segmentation
|
- post_process_instance_segmentation
|
||||||
- post_process_semantic_segmentation
|
- post_process_semantic_segmentation
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ else:
|
|||||||
("chinese_clip", ("ChineseCLIPImageProcessor", "ChineseCLIPImageProcessorFast")),
|
("chinese_clip", ("ChineseCLIPImageProcessor", "ChineseCLIPImageProcessorFast")),
|
||||||
("clip", ("CLIPImageProcessor", "CLIPImageProcessorFast")),
|
("clip", ("CLIPImageProcessor", "CLIPImageProcessorFast")),
|
||||||
("clipseg", ("ViTImageProcessor", "ViTImageProcessorFast")),
|
("clipseg", ("ViTImageProcessor", "ViTImageProcessorFast")),
|
||||||
("conditional_detr", ("ConditionalDetrImageProcessor",)),
|
("conditional_detr", ("ConditionalDetrImageProcessor", "ConditionalDetrImageProcessorFast")),
|
||||||
("convnext", ("ConvNextImageProcessor", "ConvNextImageProcessorFast")),
|
("convnext", ("ConvNextImageProcessor", "ConvNextImageProcessorFast")),
|
||||||
("convnextv2", ("ConvNextImageProcessor", "ConvNextImageProcessorFast")),
|
("convnextv2", ("ConvNextImageProcessor", "ConvNextImageProcessorFast")),
|
||||||
("cvt", ("ConvNextImageProcessor", "ConvNextImageProcessorFast")),
|
("cvt", ("ConvNextImageProcessor", "ConvNextImageProcessorFast")),
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ if TYPE_CHECKING:
|
|||||||
from .configuration_conditional_detr import *
|
from .configuration_conditional_detr import *
|
||||||
from .feature_extraction_conditional_detr import *
|
from .feature_extraction_conditional_detr import *
|
||||||
from .image_processing_conditional_detr import *
|
from .image_processing_conditional_detr import *
|
||||||
|
from .image_processing_conditional_detr_fast import *
|
||||||
from .modeling_conditional_detr import *
|
from .modeling_conditional_detr import *
|
||||||
else:
|
else:
|
||||||
import sys
|
import sys
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,137 @@
|
|||||||
|
from typing import List, Tuple, Union
|
||||||
|
|
||||||
|
from transformers.models.detr.image_processing_detr_fast import DetrImageProcessorFast
|
||||||
|
|
||||||
|
from ...image_transforms import (
|
||||||
|
center_to_corners_format,
|
||||||
|
)
|
||||||
|
from ...utils import (
|
||||||
|
TensorType,
|
||||||
|
is_torch_available,
|
||||||
|
logging,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if is_torch_available():
|
||||||
|
import torch
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ConditionalDetrImageProcessorFast(DetrImageProcessorFast):
|
||||||
|
def post_process(self, outputs, target_sizes):
|
||||||
|
"""
|
||||||
|
Converts the output of [`ConditionalDetrForObjectDetection`] into the format expected by the Pascal VOC format (xmin, ymin, xmax, ymax).
|
||||||
|
Only supports PyTorch.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
outputs ([`ConditionalDetrObjectDetectionOutput`]):
|
||||||
|
Raw outputs of the model.
|
||||||
|
target_sizes (`torch.Tensor` of shape `(batch_size, 2)`):
|
||||||
|
Tensor containing the size (h, w) of each image of the batch. For evaluation, this must be the original
|
||||||
|
image size (before any data augmentation). For visualization, this should be the image size after data
|
||||||
|
augment, but before padding.
|
||||||
|
Returns:
|
||||||
|
`List[Dict]`: A list of dictionaries, each dictionary containing the scores, labels and boxes for an image
|
||||||
|
in the batch as predicted by the model.
|
||||||
|
"""
|
||||||
|
logging.warning_once(
|
||||||
|
"`post_process` is deprecated and will be removed in v5 of Transformers, please use"
|
||||||
|
" `post_process_object_detection` instead, with `threshold=0.` for equivalent results.",
|
||||||
|
)
|
||||||
|
|
||||||
|
out_logits, out_bbox = outputs.logits, outputs.pred_boxes
|
||||||
|
|
||||||
|
if len(out_logits) != len(target_sizes):
|
||||||
|
raise ValueError("Make sure that you pass in as many target sizes as the batch dimension of the logits")
|
||||||
|
if target_sizes.shape[1] != 2:
|
||||||
|
raise ValueError("Each element of target_sizes must contain the size (h, w) of each image of the batch")
|
||||||
|
|
||||||
|
prob = out_logits.sigmoid()
|
||||||
|
topk_values, topk_indexes = torch.topk(prob.view(out_logits.shape[0], -1), 300, dim=1)
|
||||||
|
scores = topk_values
|
||||||
|
topk_boxes = torch.div(topk_indexes, out_logits.shape[2], rounding_mode="floor")
|
||||||
|
labels = topk_indexes % out_logits.shape[2]
|
||||||
|
boxes = center_to_corners_format(out_bbox)
|
||||||
|
boxes = torch.gather(boxes, 1, topk_boxes.unsqueeze(-1).repeat(1, 1, 4))
|
||||||
|
|
||||||
|
# and from relative [0, 1] to absolute [0, height] coordinates
|
||||||
|
img_h, img_w = target_sizes.unbind(1)
|
||||||
|
scale_fct = torch.stack([img_w, img_h, img_w, img_h], dim=1)
|
||||||
|
boxes = boxes * scale_fct[:, None, :]
|
||||||
|
|
||||||
|
results = [{"scores": s, "labels": l, "boxes": b} for s, l, b in zip(scores, labels, boxes)]
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def post_process_object_detection(
|
||||||
|
self, outputs, threshold: float = 0.5, target_sizes: Union[TensorType, List[Tuple]] = None, top_k: int = 100
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Converts the raw output of [`ConditionalDetrForObjectDetection`] into final bounding boxes in (top_left_x,
|
||||||
|
top_left_y, bottom_right_x, bottom_right_y) format. Only supports PyTorch.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
outputs ([`ConditionalDetrObjectDetectionOutput`]):
|
||||||
|
Raw outputs of the model.
|
||||||
|
threshold (`float`, *optional*):
|
||||||
|
Score threshold to keep object detection predictions.
|
||||||
|
target_sizes (`torch.Tensor` or `List[Tuple[int, int]]`, *optional*):
|
||||||
|
Tensor of shape `(batch_size, 2)` or list of tuples (`Tuple[int, int]`) containing the target size
|
||||||
|
(height, width) of each image in the batch. If left to None, predictions will not be resized.
|
||||||
|
top_k (`int`, *optional*, defaults to 100):
|
||||||
|
Keep only top k bounding boxes before filtering by thresholding.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
`List[Dict]`: A list of dictionaries, each dictionary containing the scores, labels and boxes for an image
|
||||||
|
in the batch as predicted by the model.
|
||||||
|
"""
|
||||||
|
out_logits, out_bbox = outputs.logits, outputs.pred_boxes
|
||||||
|
|
||||||
|
if target_sizes is not None:
|
||||||
|
if len(out_logits) != len(target_sizes):
|
||||||
|
raise ValueError(
|
||||||
|
"Make sure that you pass in as many target sizes as the batch dimension of the logits"
|
||||||
|
)
|
||||||
|
|
||||||
|
prob = out_logits.sigmoid()
|
||||||
|
prob = prob.view(out_logits.shape[0], -1)
|
||||||
|
k_value = min(top_k, prob.size(1))
|
||||||
|
topk_values, topk_indexes = torch.topk(prob, k_value, dim=1)
|
||||||
|
scores = topk_values
|
||||||
|
topk_boxes = torch.div(topk_indexes, out_logits.shape[2], rounding_mode="floor")
|
||||||
|
labels = topk_indexes % out_logits.shape[2]
|
||||||
|
boxes = center_to_corners_format(out_bbox)
|
||||||
|
boxes = torch.gather(boxes, 1, topk_boxes.unsqueeze(-1).repeat(1, 1, 4))
|
||||||
|
|
||||||
|
# and from relative [0, 1] to absolute [0, height] coordinates
|
||||||
|
if target_sizes is not None:
|
||||||
|
if isinstance(target_sizes, List):
|
||||||
|
img_h = torch.Tensor([i[0] for i in target_sizes])
|
||||||
|
img_w = torch.Tensor([i[1] for i in target_sizes])
|
||||||
|
else:
|
||||||
|
img_h, img_w = target_sizes.unbind(1)
|
||||||
|
scale_fct = torch.stack([img_w, img_h, img_w, img_h], dim=1).to(boxes.device)
|
||||||
|
boxes = boxes * scale_fct[:, None, :]
|
||||||
|
|
||||||
|
results = []
|
||||||
|
for s, l, b in zip(scores, labels, boxes):
|
||||||
|
score = s[s > threshold]
|
||||||
|
label = l[s > threshold]
|
||||||
|
box = b[s > threshold]
|
||||||
|
results.append({"scores": score, "labels": label, "boxes": box})
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def post_process_segmentation():
|
||||||
|
raise NotImplementedError("Segmentation post-processing is not implemented for Conditional DETR yet.")
|
||||||
|
|
||||||
|
def post_process_instance():
|
||||||
|
raise NotImplementedError("Instance post-processing is not implemented for Conditional DETR yet.")
|
||||||
|
|
||||||
|
def post_process_panoptic():
|
||||||
|
raise NotImplementedError("Panoptic post-processing is not implemented for Conditional DETR yet.")
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["ConditionalDetrImageProcessorFast"]
|
||||||
@@ -20,7 +20,7 @@ import unittest
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from transformers.testing_utils import require_torch, require_vision, slow
|
from transformers.testing_utils import require_torch, require_vision, slow
|
||||||
from transformers.utils import is_torch_available, is_vision_available
|
from transformers.utils import is_torch_available, is_torchvision_available, is_vision_available
|
||||||
|
|
||||||
from ...test_image_processing_common import AnnotationFormatTestMixin, ImageProcessingTestMixin, prepare_image_inputs
|
from ...test_image_processing_common import AnnotationFormatTestMixin, ImageProcessingTestMixin, prepare_image_inputs
|
||||||
|
|
||||||
@@ -33,6 +33,9 @@ if is_vision_available():
|
|||||||
|
|
||||||
from transformers import ConditionalDetrImageProcessor
|
from transformers import ConditionalDetrImageProcessor
|
||||||
|
|
||||||
|
if is_torchvision_available():
|
||||||
|
from transformers import ConditionalDetrImageProcessorFast
|
||||||
|
|
||||||
|
|
||||||
class ConditionalDetrImageProcessingTester:
|
class ConditionalDetrImageProcessingTester:
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -132,6 +135,7 @@ class ConditionalDetrImageProcessingTester:
|
|||||||
@require_vision
|
@require_vision
|
||||||
class ConditionalDetrImageProcessingTest(AnnotationFormatTestMixin, ImageProcessingTestMixin, unittest.TestCase):
|
class ConditionalDetrImageProcessingTest(AnnotationFormatTestMixin, ImageProcessingTestMixin, unittest.TestCase):
|
||||||
image_processing_class = ConditionalDetrImageProcessor if is_vision_available() else None
|
image_processing_class = ConditionalDetrImageProcessor if is_vision_available() else None
|
||||||
|
fast_image_processing_class = ConditionalDetrImageProcessorFast if is_torchvision_available() else None
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
@@ -142,7 +146,8 @@ class ConditionalDetrImageProcessingTest(AnnotationFormatTestMixin, ImageProcess
|
|||||||
return self.image_processor_tester.prepare_image_processor_dict()
|
return self.image_processor_tester.prepare_image_processor_dict()
|
||||||
|
|
||||||
def test_image_processor_properties(self):
|
def test_image_processor_properties(self):
|
||||||
image_processing = self.image_processing_class(**self.image_processor_dict)
|
for image_processing_class in self.image_processor_list:
|
||||||
|
image_processing = image_processing_class(**self.image_processor_dict)
|
||||||
self.assertTrue(hasattr(image_processing, "image_mean"))
|
self.assertTrue(hasattr(image_processing, "image_mean"))
|
||||||
self.assertTrue(hasattr(image_processing, "image_std"))
|
self.assertTrue(hasattr(image_processing, "image_std"))
|
||||||
self.assertTrue(hasattr(image_processing, "do_normalize"))
|
self.assertTrue(hasattr(image_processing, "do_normalize"))
|
||||||
@@ -150,11 +155,12 @@ class ConditionalDetrImageProcessingTest(AnnotationFormatTestMixin, ImageProcess
|
|||||||
self.assertTrue(hasattr(image_processing, "size"))
|
self.assertTrue(hasattr(image_processing, "size"))
|
||||||
|
|
||||||
def test_image_processor_from_dict_with_kwargs(self):
|
def test_image_processor_from_dict_with_kwargs(self):
|
||||||
image_processor = self.image_processing_class.from_dict(self.image_processor_dict)
|
for image_processing_class in self.image_processor_list:
|
||||||
|
image_processor = image_processing_class.from_dict(self.image_processor_dict)
|
||||||
self.assertEqual(image_processor.size, {"shortest_edge": 18, "longest_edge": 1333})
|
self.assertEqual(image_processor.size, {"shortest_edge": 18, "longest_edge": 1333})
|
||||||
self.assertEqual(image_processor.do_pad, True)
|
self.assertEqual(image_processor.do_pad, True)
|
||||||
|
|
||||||
image_processor = self.image_processing_class.from_dict(
|
image_processor = image_processing_class.from_dict(
|
||||||
self.image_processor_dict, size=42, max_size=84, pad_and_return_pixel_mask=False
|
self.image_processor_dict, size=42, max_size=84, pad_and_return_pixel_mask=False
|
||||||
)
|
)
|
||||||
self.assertEqual(image_processor.size, {"shortest_edge": 42, "longest_edge": 84})
|
self.assertEqual(image_processor.size, {"shortest_edge": 42, "longest_edge": 84})
|
||||||
@@ -169,8 +175,9 @@ class ConditionalDetrImageProcessingTest(AnnotationFormatTestMixin, ImageProcess
|
|||||||
|
|
||||||
target = {"image_id": 39769, "annotations": target}
|
target = {"image_id": 39769, "annotations": target}
|
||||||
|
|
||||||
|
for image_processing_class in self.image_processor_list:
|
||||||
# encode them
|
# encode them
|
||||||
image_processing = ConditionalDetrImageProcessor.from_pretrained("microsoft/conditional-detr-resnet-50")
|
image_processing = image_processing_class.from_pretrained("microsoft/conditional-detr-resnet-50")
|
||||||
encoding = image_processing(images=image, annotations=target, return_tensors="pt")
|
encoding = image_processing(images=image, annotations=target, return_tensors="pt")
|
||||||
|
|
||||||
# verify pixel values
|
# verify pixel values
|
||||||
@@ -215,8 +222,9 @@ class ConditionalDetrImageProcessingTest(AnnotationFormatTestMixin, ImageProcess
|
|||||||
|
|
||||||
masks_path = pathlib.Path("./tests/fixtures/tests_samples/COCO/coco_panoptic")
|
masks_path = pathlib.Path("./tests/fixtures/tests_samples/COCO/coco_panoptic")
|
||||||
|
|
||||||
|
for image_processing_class in self.image_processor_list:
|
||||||
# encode them
|
# encode them
|
||||||
image_processing = ConditionalDetrImageProcessor(format="coco_panoptic")
|
image_processing = image_processing_class(format="coco_panoptic")
|
||||||
encoding = image_processing(images=image, annotations=target, masks_path=masks_path, return_tensors="pt")
|
encoding = image_processing(images=image, annotations=target, masks_path=masks_path, return_tensors="pt")
|
||||||
|
|
||||||
# verify pixel values
|
# verify pixel values
|
||||||
@@ -245,7 +253,8 @@ class ConditionalDetrImageProcessingTest(AnnotationFormatTestMixin, ImageProcess
|
|||||||
torch.testing.assert_close(encoding["labels"][0]["class_labels"], expected_class_labels)
|
torch.testing.assert_close(encoding["labels"][0]["class_labels"], expected_class_labels)
|
||||||
# verify masks
|
# verify masks
|
||||||
expected_masks_sum = 822873
|
expected_masks_sum = 822873
|
||||||
self.assertEqual(encoding["labels"][0]["masks"].sum().item(), expected_masks_sum)
|
relative_error = torch.abs(encoding["labels"][0]["masks"].sum() - expected_masks_sum) / expected_masks_sum
|
||||||
|
self.assertTrue(relative_error < 1e-3)
|
||||||
# verify orig_size
|
# verify orig_size
|
||||||
expected_orig_size = torch.tensor([480, 640])
|
expected_orig_size = torch.tensor([480, 640])
|
||||||
torch.testing.assert_close(encoding["labels"][0]["orig_size"], expected_orig_size)
|
torch.testing.assert_close(encoding["labels"][0]["orig_size"], expected_orig_size)
|
||||||
|
|||||||
Reference in New Issue
Block a user