Source code for torchvision.transforms.v2.functional._meta
fromtypingimportList,Optional,TupleimportPIL.Imageimporttorchfromtorchvisionimporttv_tensorsfromtorchvision.transformsimport_functional_pilas_FPfromtorchvision.tv_tensorsimportBoundingBoxFormatfromtorchvision.utilsimport_log_api_usage_oncefrom._utilsimport_get_kernel,_register_kernel_internal,is_pure_tensordefget_dimensions(inpt:torch.Tensor)->List[int]:iftorch.jit.is_scripting():returnget_dimensions_image(inpt)_log_api_usage_once(get_dimensions)kernel=_get_kernel(get_dimensions,type(inpt))returnkernel(inpt)@_register_kernel_internal(get_dimensions,torch.Tensor)@_register_kernel_internal(get_dimensions,tv_tensors.Image,tv_tensor_wrapper=False)defget_dimensions_image(image:torch.Tensor)->List[int]:chw=list(image.shape[-3:])ndims=len(chw)ifndims==3:returnchwelifndims==2:chw.insert(0,1)returnchwelse:raiseTypeError(f"Input tensor should have at least two dimensions, but got {ndims}")_get_dimensions_image_pil=_register_kernel_internal(get_dimensions,PIL.Image.Image)(_FP.get_dimensions)@_register_kernel_internal(get_dimensions,tv_tensors.Video,tv_tensor_wrapper=False)defget_dimensions_video(video:torch.Tensor)->List[int]:returnget_dimensions_image(video)defget_num_channels(inpt:torch.Tensor)->int:iftorch.jit.is_scripting():returnget_num_channels_image(inpt)_log_api_usage_once(get_num_channels)kernel=_get_kernel(get_num_channels,type(inpt))returnkernel(inpt)@_register_kernel_internal(get_num_channels,torch.Tensor)@_register_kernel_internal(get_num_channels,tv_tensors.Image,tv_tensor_wrapper=False)defget_num_channels_image(image:torch.Tensor)->int:chw=image.shape[-3:]ndims=len(chw)ifndims==3:returnchw[0]elifndims==2:return1else:raiseTypeError(f"Input tensor should have at least two dimensions, but got {ndims}")_get_num_channels_image_pil=_register_kernel_internal(get_num_channels,PIL.Image.Image)(_FP.get_image_num_channels)@_register_kernel_internal(get_num_channels,tv_tensors.Video,tv_tensor_wrapper=False)defget_num_channels_video(video:torch.Tensor)->int:returnget_num_channels_image(video)# We changed the names to ensure it can be used not only for images but also videos. Thus, we just alias it without# deprecating the old names.get_image_num_channels=get_num_channelsdefget_size(inpt:torch.Tensor)->List[int]:iftorch.jit.is_scripting():returnget_size_image(inpt)_log_api_usage_once(get_size)kernel=_get_kernel(get_size,type(inpt))returnkernel(inpt)@_register_kernel_internal(get_size,torch.Tensor)@_register_kernel_internal(get_size,tv_tensors.Image,tv_tensor_wrapper=False)defget_size_image(image:torch.Tensor)->List[int]:hw=list(image.shape[-2:])ndims=len(hw)ifndims==2:returnhwelse:raiseTypeError(f"Input tensor should have at least two dimensions, but got {ndims}")@_register_kernel_internal(get_size,PIL.Image.Image)def_get_size_image_pil(image:PIL.Image.Image)->List[int]:width,height=_FP.get_image_size(image)return[height,width]@_register_kernel_internal(get_size,tv_tensors.Video,tv_tensor_wrapper=False)defget_size_video(video:torch.Tensor)->List[int]:returnget_size_image(video)@_register_kernel_internal(get_size,tv_tensors.Mask,tv_tensor_wrapper=False)defget_size_mask(mask:torch.Tensor)->List[int]:returnget_size_image(mask)@_register_kernel_internal(get_size,tv_tensors.BoundingBoxes,tv_tensor_wrapper=False)defget_size_bounding_boxes(bounding_box:tv_tensors.BoundingBoxes)->List[int]:returnlist(bounding_box.canvas_size)defget_num_frames(inpt:torch.Tensor)->int:iftorch.jit.is_scripting():returnget_num_frames_video(inpt)_log_api_usage_once(get_num_frames)kernel=_get_kernel(get_num_frames,type(inpt))returnkernel(inpt)@_register_kernel_internal(get_num_frames,torch.Tensor)@_register_kernel_internal(get_num_frames,tv_tensors.Video,tv_tensor_wrapper=False)defget_num_frames_video(video:torch.Tensor)->int:returnvideo.shape[-4]def_xywh_to_xyxy(xywh:torch.Tensor,inplace:bool)->torch.Tensor:xyxy=xywhifinplaceelsexywh.clone()xyxy[...,2:]+=xyxy[...,:2]returnxyxydef_xyxy_to_xywh(xyxy:torch.Tensor,inplace:bool)->torch.Tensor:xywh=xyxyifinplaceelsexyxy.clone()xywh[...,2:]-=xywh[...,:2]returnxywhdef_cxcywh_to_xyxy(cxcywh:torch.Tensor,inplace:bool)->torch.Tensor:ifnotinplace:cxcywh=cxcywh.clone()# Trick to do fast division by 2 and ceil, without casting. It produces the same result as# `torchvision.ops._box_convert._box_cxcywh_to_xyxy`.half_wh=cxcywh[...,2:].div(-2,rounding_mode=Noneifcxcywh.is_floating_point()else"floor").abs_()# (cx - width / 2) = x1, same for y1cxcywh[...,:2].sub_(half_wh)# (x1 + width) = x2, same for y2cxcywh[...,2:].add_(cxcywh[...,:2])returncxcywhdef_xyxy_to_cxcywh(xyxy:torch.Tensor,inplace:bool)->torch.Tensor:ifnotinplace:xyxy=xyxy.clone()# (x2 - x1) = width, same for heightxyxy[...,2:].sub_(xyxy[...,:2])# (x1 * 2 + width) / 2 = x1 + width / 2 = x1 + (x2-x1)/2 = (x1 + x2)/2 = cx, same for cyxyxy[...,:2].mul_(2).add_(xyxy[...,2:]).div_(2,rounding_mode=Noneifxyxy.is_floating_point()else"floor")returnxyxydef_convert_bounding_box_format(bounding_boxes:torch.Tensor,old_format:BoundingBoxFormat,new_format:BoundingBoxFormat,inplace:bool=False)->torch.Tensor:ifnew_format==old_format:returnbounding_boxes# TODO: Add _xywh_to_cxcywh and _cxcywh_to_xywh to improve performanceifold_format==BoundingBoxFormat.XYWH:bounding_boxes=_xywh_to_xyxy(bounding_boxes,inplace)elifold_format==BoundingBoxFormat.CXCYWH:bounding_boxes=_cxcywh_to_xyxy(bounding_boxes,inplace)ifnew_format==BoundingBoxFormat.XYWH:bounding_boxes=_xyxy_to_xywh(bounding_boxes,inplace)elifnew_format==BoundingBoxFormat.CXCYWH:bounding_boxes=_xyxy_to_cxcywh(bounding_boxes,inplace)returnbounding_boxes
[docs]defconvert_bounding_box_format(inpt:torch.Tensor,old_format:Optional[BoundingBoxFormat]=None,new_format:Optional[BoundingBoxFormat]=None,inplace:bool=False,)->torch.Tensor:"""See :func:`~torchvision.transforms.v2.ConvertBoundingBoxFormat` for details."""# This being a kernel / functional hybrid, we need an option to pass `old_format` explicitly for pure tensor# inputs as well as extract it from `tv_tensors.BoundingBoxes` inputs. However, putting a default value on# `old_format` means we also need to put one on `new_format` to have syntactically correct Python. Here we mimic the# default error that would be thrown if `new_format` had no default value.ifnew_formatisNone:raiseTypeError("convert_bounding_box_format() missing 1 required argument: 'new_format'")ifnottorch.jit.is_scripting():_log_api_usage_once(convert_bounding_box_format)ifisinstance(old_format,str):old_format=BoundingBoxFormat[old_format.upper()]ifisinstance(new_format,str):new_format=BoundingBoxFormat[new_format.upper()]iftorch.jit.is_scripting()oris_pure_tensor(inpt):ifold_formatisNone:raiseValueError("For pure tensor inputs, `old_format` has to be passed.")return_convert_bounding_box_format(inpt,old_format=old_format,new_format=new_format,inplace=inplace)elifisinstance(inpt,tv_tensors.BoundingBoxes):ifold_formatisnotNone:raiseValueError("For bounding box tv_tensor inputs, `old_format` must not be passed.")output=_convert_bounding_box_format(inpt.as_subclass(torch.Tensor),old_format=inpt.format,new_format=new_format,inplace=inplace)returntv_tensors.wrap(output,like=inpt,format=new_format)else:raiseTypeError(f"Input can either be a plain tensor or a bounding box tv_tensor, but got {type(inpt)} instead.")
def_clamp_bounding_boxes(bounding_boxes:torch.Tensor,format:BoundingBoxFormat,canvas_size:Tuple[int,int])->torch.Tensor:# TODO: Investigate if it makes sense from a performance perspective to have an implementation for every# BoundingBoxFormat instead of converting back and forthin_dtype=bounding_boxes.dtypebounding_boxes=bounding_boxes.clone()ifbounding_boxes.is_floating_point()elsebounding_boxes.float()xyxy_boxes=convert_bounding_box_format(bounding_boxes,old_format=format,new_format=tv_tensors.BoundingBoxFormat.XYXY,inplace=True)xyxy_boxes[...,0::2].clamp_(min=0,max=canvas_size[1])xyxy_boxes[...,1::2].clamp_(min=0,max=canvas_size[0])out_boxes=convert_bounding_box_format(xyxy_boxes,old_format=BoundingBoxFormat.XYXY,new_format=format,inplace=True)returnout_boxes.to(in_dtype)
[docs]defclamp_bounding_boxes(inpt:torch.Tensor,format:Optional[BoundingBoxFormat]=None,canvas_size:Optional[Tuple[int,int]]=None,)->torch.Tensor:"""See :func:`~torchvision.transforms.v2.ClampBoundingBoxes` for details."""ifnottorch.jit.is_scripting():_log_api_usage_once(clamp_bounding_boxes)iftorch.jit.is_scripting()oris_pure_tensor(inpt):ifformatisNoneorcanvas_sizeisNone:raiseValueError("For pure tensor inputs, `format` and `canvas_size` have to be passed.")return_clamp_bounding_boxes(inpt,format=format,canvas_size=canvas_size)elifisinstance(inpt,tv_tensors.BoundingBoxes):ifformatisnotNoneorcanvas_sizeisnotNone:raiseValueError("For bounding box tv_tensor inputs, `format` and `canvas_size` must not be passed.")output=_clamp_bounding_boxes(inpt.as_subclass(torch.Tensor),format=inpt.format,canvas_size=inpt.canvas_size)returntv_tensors.wrap(output,like=inpt)else:raiseTypeError(f"Input can either be a plain tensor or a bounding box tv_tensor, but got {type(inpt)} instead.")
Docs
Access comprehensive developer documentation for PyTorch
To analyze traffic and optimize your experience, we serve cookies on this site. By clicking or navigating, you agree to allow our usage of cookies. As the current maintainers of this site, Facebook’s Cookies Policy applies. Learn more, including about available controls: Cookies Policy.