11from array import array
2- from enum import Enum
3- from typing import List , Optional , Tuple
2+ from typing import Any , List , Optional , Tuple
43
5- from typing_extensions import Self
4+ from typing_extensions import ClassVar , Self
65
76from viam .errors import NotSupportedError
87from viam .proto .component .camera import Format
98
109from .viam_rgba import RGBA_HEADER_LENGTH , RGBA_MAGIC_NUMBER
1110
1211
13- class CameraMimeType (str , Enum ):
14- VIAM_RGBA = "image/vnd.viam.rgba"
15- VIAM_RAW_DEPTH = "image/vnd.viam.dep"
16- JPEG = "image/jpeg"
17- PNG = "image/png"
18- PCD = "pointcloud/pcd"
12+ class _FrozenClassAttributesMeta (type ):
13+ """
14+ A metaclass that prevents the reassignment of existing class attributes.
15+ """
16+
17+ def __setattr__ (cls , name : str , value : Any ):
18+ # Check if the attribute `name` already exists on the class
19+ if name in cls .__dict__ :
20+ # If it exists, raise an error to prevent overwriting
21+ raise AttributeError (f"Cannot reassign constant '{ name } '" )
22+ # If it's a new attribute, allow it to be set
23+ super ().__setattr__ (name , value )
24+
25+
26+ class CameraMimeType (str , metaclass = _FrozenClassAttributesMeta ):
27+ """
28+ The compatible mime-types for cameras and vision services.
29+
30+ You can use the `CameraMimeType.CUSTOM(...)` method to use an unlisted mime-type.
31+ """
32+
33+ VIAM_RGBA : ClassVar [Self ]
34+ VIAM_RAW_DEPTH : ClassVar [Self ]
35+ JPEG : ClassVar [Self ]
36+ PNG : ClassVar [Self ]
37+ PCD : ClassVar [Self ]
38+
39+ @property
40+ def name (self ) -> str :
41+ for key , value in self .__class__ .__dict__ .items ():
42+ if value == self :
43+ return key
44+ return "CUSTOM"
45+
46+ @property
47+ def value (self ) -> str :
48+ return self
49+
50+ @classmethod
51+ def CUSTOM (cls , mime_type : str ) -> Self :
52+ """
53+ Create a custom mime type.
54+
55+ Args:
56+ mime_type (str): The mimetype as a string
57+ """
58+ return cls .from_string (mime_type )
1959
2060 @classmethod
2161 def from_string (cls , value : str ) -> Self :
@@ -28,13 +68,10 @@ def from_string(cls, value: str) -> Self:
2868 Self: The mimetype
2969 """
3070 value_mime = value [:- 5 ] if value .endswith ("+lazy" ) else value # ViamImage lazy encodes by default
31- try :
32- return cls (value_mime )
33- except ValueError :
34- raise ValueError (f"Invalid mimetype: { value } " )
71+ return cls (value_mime )
3572
3673 @classmethod
37- def from_proto (cls , format : Format .ValueType ) -> "CameraMimeType" :
74+ def from_proto (cls , format : Format .ValueType ) -> Self :
3875 """Returns the mimetype from a proto enum.
3976
4077 Args:
@@ -44,14 +81,15 @@ def from_proto(cls, format: Format.ValueType) -> "CameraMimeType":
4481 Self: The mimetype.
4582 """
4683 mimetypes = {
47- Format .FORMAT_RAW_RGBA : CameraMimeType .VIAM_RGBA ,
48- Format .FORMAT_RAW_DEPTH : CameraMimeType .VIAM_RAW_DEPTH ,
49- Format .FORMAT_JPEG : CameraMimeType .JPEG ,
50- Format .FORMAT_PNG : CameraMimeType .PNG ,
84+ Format .FORMAT_RAW_RGBA : cls .VIAM_RGBA ,
85+ Format .FORMAT_RAW_DEPTH : cls .VIAM_RAW_DEPTH ,
86+ Format .FORMAT_JPEG : cls .JPEG ,
87+ Format .FORMAT_PNG : cls .PNG ,
5188 }
52- return mimetypes .get (format , CameraMimeType .JPEG )
89+ return cls ( mimetypes .get (format , cls .JPEG ) )
5390
54- def to_proto (self ) -> Format .ValueType :
91+ @property
92+ def proto (self ) -> Format .ValueType :
5593 """Returns the mimetype in a proto enum.
5694
5795 Returns:
@@ -65,6 +103,19 @@ def to_proto(self) -> Format.ValueType:
65103 }
66104 return formats .get (self , Format .FORMAT_UNSPECIFIED )
67105
106+ def to_proto (self ) -> Format .ValueType :
107+ """
108+ DEPRECATED: Use `CameraMimeType.proto`
109+ """
110+ return self .proto
111+
112+
113+ CameraMimeType .VIAM_RGBA = CameraMimeType .from_string ("image/vnd.viam.rgba" )
114+ CameraMimeType .VIAM_RAW_DEPTH = CameraMimeType .from_string ("image/vnd.viam.dep" )
115+ CameraMimeType .JPEG = CameraMimeType .from_string ("image/jpeg" )
116+ CameraMimeType .PNG = CameraMimeType .from_string ("image/png" )
117+ CameraMimeType .PCD = CameraMimeType .from_string ("pointcloud/pcd" )
118+
68119
69120class ViamImage :
70121 """A native implementation of an image.
@@ -73,11 +124,11 @@ class ViamImage:
73124 """
74125
75126 _data : bytes
76- _mime_type : str
127+ _mime_type : CameraMimeType
77128 _height : Optional [int ] = None
78129 _width : Optional [int ] = None
79130
80- def __init__ (self , data : bytes , mime_type : str ) -> None :
131+ def __init__ (self , data : bytes , mime_type : CameraMimeType ) -> None :
81132 self ._data = data
82133 self ._mime_type = mime_type
83134 self ._width , self ._height = _getDimensions (data , mime_type )
@@ -88,7 +139,7 @@ def data(self) -> bytes:
88139 return self ._data
89140
90141 @property
91- def mime_type (self ) -> str :
142+ def mime_type (self ) -> CameraMimeType :
92143 """The mime type of the image"""
93144 return self ._mime_type
94145
@@ -131,12 +182,12 @@ class NamedImage(ViamImage):
131182 """The name of the image
132183 """
133184
134- def __init__ (self , name : str , data : bytes , mime_type : str ) -> None :
185+ def __init__ (self , name : str , data : bytes , mime_type : CameraMimeType ) -> None :
135186 self .name = name
136187 super ().__init__ (data , mime_type )
137188
138189
139- def _getDimensions (image : bytes , mime_type : str ) -> Tuple [Optional [int ], Optional [int ]]:
190+ def _getDimensions (image : bytes , mime_type : CameraMimeType ) -> Tuple [Optional [int ], Optional [int ]]:
140191 try :
141192 if mime_type == CameraMimeType .JPEG :
142193 return _getDimensionsFromJPEG (image )
0 commit comments