delayed_image.sensorchan_spec module¶
This is an extension of delayed_image.channel_spec, which augments channel
information with an associated sensor attribute. Eventually, this will entirely
replace the channel spec.
Example
>>> # xdoctest: +REQUIRES(module:lark)
>>> # hack for 3.6
>>> from delayed_image import sensorchan_spec
>>> import delayed_image
>>> delayed_image.SensorChanSpec = sensorchan_spec.SensorChanSpec
>>> self = delayed_image.SensorChanSpec.coerce('sensor0:B1|B8|B8a|B10|B11,sensor1:B11|X.2|Y:2:6,sensor2:r|g|b|disparity|gauss|B8|B11,sensor3:r|g|b|flowx|flowy|distri|B10|B11')
>>> self.normalize()
- delayed_image.sensorchan_spec.SENSOR_CHAN_GRAMMAR = '// SENSOR_CHAN_GRAMMAR\n?start: stream\n\n// An identifier can contain spaces\nIDEN: ("_"|"*"|LETTER) ("_"|" "|"-"|"*"|LETTER|DIGIT)*\n\nchan_single : IDEN\nchan_getitem : IDEN "." INT\nchan_getslice_0b : IDEN ":" INT\nchan_getslice_ab : (IDEN "." INT ":" INT) | (IDEN ":" INT ":" INT)\n\n// A channel code can just be an ID, or it can have a getitem\n// style syntax with a scalar or slice as an argument\nchan_code : chan_single | chan_getslice_0b | chan_getslice_ab | chan_getitem\n\n// Fused channels are an ordered sequence of channel codes (without sensors)\nfused : chan_code ("|" chan_code)*\n\n// Channels can be specified in a sequence but must contain parens\nfused_seq : "(" fused ("," fused)* ")"\n\n// Sensors can be specified in a sequence but must contain parens\nsensor_seq : "(" IDEN ("," IDEN)* "):"\n\nsensor_lhs : (IDEN ":") | (sensor_seq)\n\n// A channel only part can be a fused channel or a sequence\nchannel_rhs : fused | fused_seq\n\nsensor_chan : sensor_lhs channel_rhs?\n\nnosensor_chan : channel_rhs\n\nstream_item : sensor_chan | nosensor_chan\n\n// A stream is an unordered sequence of fused channels, that can\n// optionally contain sensor specifications.\n\nstream : stream_item ("," stream_item)*\n\n%import common.DIGIT\n%import common.LETTER\n%import common.INT'¶
add the concept of an exclusive or operator with left hand priority. The idea is that we can specify a code that will use the one fused channel spec if it is available, but if it is not, it will fall back to the next one. E.G.
WV:((red|green|blue)^(pan))
(L8,S2,WV,WV1):((red|green|blue)^(pan))
Possible Production Rules:
fused : chan_code (“|” chan_code)* fused_seq : “(” fused (“,” fused)* “)”
Maybe also include that on the sensor side?
Use WV:r|g|b if we have it otherwise use S2:r|g|b
(WV^S2)(r|g|b)
- Type:
TODO
- class delayed_image.sensorchan_spec.SensorSpec(spec)[source]¶
Bases:
NiceReprA simple wrapper for sensors in case we want to do anything fancy with them later. For now they are just a string.
- class delayed_image.sensorchan_spec.SensorChanSpec(spec: str)[source]¶
Bases:
NiceReprThe public facing API for the sensor / channel specification
Example
>>> # xdoctest: +REQUIRES(module:lark) >>> from delayed_image.sensorchan_spec import SensorChanSpec >>> self = SensorChanSpec('(L8,S2):BGR,WV:BGR,S2:nir,L8:land.0:4') >>> s1 = self.normalize() >>> s2 = self.concise() >>> streams = self.streams() >>> print(s1) >>> print(s2) >>> print('streams = {}'.format(ub.urepr(streams, sv=1, nl=1))) L8:BGR,S2:BGR,WV:BGR,S2:nir,L8:land.0|land.1|land.2|land.3 (L8,S2,WV):BGR,L8:land:4,S2:nir streams = [ L8:BGR, S2:BGR, WV:BGR, S2:nir, L8:land.0|land.1|land.2|land.3, ]
Example
>>> # Check with generic sensors >>> # xdoctest: +REQUIRES(module:lark) >>> from delayed_image.sensorchan_spec import SensorChanSpec >>> import delayed_image >>> self = SensorChanSpec('(*):BGR,*:BGR,*:nir,*:land.0:4') >>> self.concise().normalize() >>> s1 = self.normalize() >>> s2 = self.concise() >>> print(s1) >>> print(s2) *:BGR,*:BGR,*:nir,*:land.0|land.1|land.2|land.3 (*):BGR,*:(nir,land:4) >>> import delayed_image >>> c = delayed_image.ChannelSpec.coerce('BGR,BGR,nir,land.0:8') >>> c1 = c.normalize() >>> c2 = c.concise() >>> print(c1) >>> print(c2)
Example
>>> # Check empty channels >>> # xdoctest: +REQUIRES(module:lark) >>> from delayed_image.sensorchan_spec import SensorChanSpec >>> import delayed_image >>> print(SensorChanSpec('*:').normalize()) *: >>> print(SensorChanSpec('sen:').normalize()) sen: >>> print(SensorChanSpec('sen:').normalize().concise()) sen: >>> print(SensorChanSpec('sen:').concise().normalize().concise()) sen:
- classmethod coerce(data)[source]¶
Attempt to interpret the data as a channel specification
- Returns:
SensorChanSpec
Example
>>> # xdoctest: +REQUIRES(module:lark) >>> from delayed_image.sensorchan_spec import * # NOQA >>> from delayed_image.sensorchan_spec import SensorChanSpec >>> data = SensorChanSpec.coerce(3) >>> assert SensorChanSpec.coerce(data).normalize().spec == '*:u0|u1|u2' >>> data = SensorChanSpec.coerce(3) >>> assert data.spec == 'u0|u1|u2' >>> assert SensorChanSpec.coerce(data).spec == 'u0|u1|u2' >>> data = SensorChanSpec.coerce('u:3') >>> assert data.normalize().spec == '*:u.0|u.1|u.2'
- concise()[source]¶
Example
>>> # xdoctest: +REQUIRES(module:lark) >>> from delayed_image import SensorChanSpec >>> a = SensorChanSpec.coerce('Cam1:(red,blue)') >>> b = SensorChanSpec.coerce('Cam2:(blue,green)') >>> c = (a + b).concise() >>> print(c) (Cam1,Cam2):blue,Cam1:red,Cam2:green >>> # Note the importance of parenthesis in the previous example >>> # otherwise channels will be assigned to `*` the generic sensor. >>> a = SensorChanSpec.coerce('Cam1:red,blue') >>> b = SensorChanSpec.coerce('Cam2:blue,green') >>> c = (a + b).concise() >>> print(c) (*,Cam2):blue,*:green,Cam1:red
- streams()[source]¶
- Returns:
List of sensor-names and fused channel specs
- Return type:
List[FusedSensorChanSpec]
- late_fuse(*others)[source]¶
Example
>>> # xdoctest: +REQUIRES(module:lark) >>> import delayed_image >>> from delayed_image import sensorchan_spec >>> import delayed_image >>> delayed_image.SensorChanSpec = sensorchan_spec.SensorChanSpec # hack for 3.6 >>> a = delayed_image.SensorChanSpec.coerce('A|B|C,edf') >>> b = delayed_image.SensorChanSpec.coerce('A12') >>> c = delayed_image.SensorChanSpec.coerce('') >>> d = delayed_image.SensorChanSpec.coerce('rgb') >>> print(a.late_fuse(b).spec) >>> print((a + b).spec) >>> print((b + a).spec) >>> print((a + b + c).spec) >>> print(sum([a, b, c, d]).spec) A|B|C,edf,A12 A|B|C,edf,A12 A12,A|B|C,edf A|B|C,edf,A12 A|B|C,edf,A12,rgb >>> import delayed_image >>> a = delayed_image.SensorChanSpec.coerce('A|B|C,edf').normalize() >>> b = delayed_image.SensorChanSpec.coerce('A12').normalize() >>> c = delayed_image.SensorChanSpec.coerce('').normalize() >>> d = delayed_image.SensorChanSpec.coerce('rgb').normalize() >>> print(a.late_fuse(b).spec) >>> print((a + b).spec) >>> print((b + a).spec) >>> print((a + b + c).spec) >>> print(sum([a, b, c, d]).spec) *:A|B|C,*:edf,*:A12 *:A|B|C,*:edf,*:A12 *:A12,*:A|B|C,*:edf *:A|B|C,*:edf,*:A12,*: *:A|B|C,*:edf,*:A12,*:,*:rgb >>> print((a.late_fuse(b)).concise()) >>> print(((a + b)).concise()) >>> print(((b + a)).concise()) >>> print(((a + b + c)).concise()) >>> print((sum([a, b, c, d])).concise()) *:(A|B|C,edf,A12) *:(A|B|C,edf,A12) *:(A12,A|B|C,edf) *:(A|B|C,edf,A12,) *:(A|B|C,edf,A12,,r|g|b)
Example
>>> # Test multi-arg case >>> import delayed_image >>> a = delayed_image.SensorChanSpec.coerce('A|B|C,edf') >>> b = delayed_image.SensorChanSpec.coerce('A12') >>> c = delayed_image.SensorChanSpec.coerce('') >>> d = delayed_image.SensorChanSpec.coerce('rgb') >>> others = [b, c, d] >>> print(a.late_fuse(*others).spec) >>> print(delayed_image.SensorChanSpec.late_fuse(a, b, c, d).spec) A|B|C,edf,A12,rgb A|B|C,edf,A12,rgb
- matching_sensor(sensor)[source]¶
Get the components corresponding to a specific sensor
- Parameters:
sensor (str) – the name of the sensor to match
Example
>>> # xdoctest: +REQUIRES(module:lark) >>> import delayed_image >>> self = delayed_image.SensorChanSpec.coerce('(S1,S2):(a|b|c),S2:c|d|e') >>> sensor = 'S2' >>> new = self.matching_sensor(sensor) >>> print(f'new={new}') new=S2:a|b|c,S2:c|d|e >>> print(self.matching_sensor('S1')) S1:a|b|c >>> print(self.matching_sensor('S3')) S3:
- property chans¶
Returns the channel-only spec, ONLY if all of the sensors are the same
Example
>>> # xdoctest: +REQUIRES(module:lark) >>> import delayed_image >>> self = delayed_image.SensorChanSpec.coerce('(S1,S2):(a|b|c),S2:c|d|e') >>> import pytest >>> with pytest.raises(Exception): >>> self.chans >>> print(self.matching_sensor('S1').chans.spec) >>> print(self.matching_sensor('S2').chans.spec) a|b|c a|b|c,c|d|e
- class delayed_image.sensorchan_spec.FusedSensorChanSpec(sensor, chans)[source]¶
Bases:
SensorChanSpecA single sensor a corresponding fused channels.
- property chans¶
- property spec¶
- class delayed_image.sensorchan_spec.SensorChanNode(sensor, chan)[source]¶
Bases:
objectTODO: just replace this with the spec class itself?
- property spec¶
- class delayed_image.sensorchan_spec.FusedChanNode(chan)[source]¶
Bases:
objectTODO: just replace this with the spec class itself?
Example
s = FusedChanNode(‘a|b|c.0|c.1|c.2’) c = s.concise() print(s) print(c)
- property spec¶
- class delayed_image.sensorchan_spec.SensorChanTransformer(concise_channels=1, concise_sensors=1)[source]¶
Bases:
TransformerGiven a parsed tree for a sensor-chan spec, can transform it into useful forms.
Todo
Make the classes that hold the underlying data more robust such that they either use the existing channel spec or entirely replace it. (probably the former). Also need to add either a FusedSensorChan node that is restircted to only a single sensor and group of fused channels.
- delayed_image.sensorchan_spec.normalize_sensor_chan(spec: str)[source]¶
Example
>>> # xdoctest: +REQUIRES(module:lark) >>> from delayed_image.sensorchan_spec import * # NOQA >>> spec = 'L8:mat:4,L8:red,S2:red,S2:forest|brush,S2:mat.0|mat.1|mat.2|mat.3' >>> r1 = normalize_sensor_chan(spec) >>> spec = 'L8:r|g|b,L8:r|g|b' >>> r2 = normalize_sensor_chan(spec) >>> print(f'r1={r1}') >>> print(f'r2={r2}') r1=L8:mat.0|mat.1|mat.2|mat.3,L8:red,S2:red,S2:forest|brush,S2:mat.0|mat.1|mat.2|mat.3 r2=L8:r|g|b,L8:r|g|b
- delayed_image.sensorchan_spec.concise_sensor_chan(spec: str)[source]¶
Example
>>> # xdoctest: +REQUIRES(module:lark) >>> from delayed_image.sensorchan_spec import * # NOQA >>> spec = 'L8:mat.0|mat.1|mat.2|mat.3,L8:red,S2:red,S2:forest|brush,S2:mat.0|mat.1|mat.2|mat.3' >>> concise_spec = concise_sensor_chan(spec) >>> normed_spec = normalize_sensor_chan(concise_spec) >>> concise_spec2 = concise_sensor_chan(normed_spec) >>> assert concise_spec2 == concise_spec >>> print(concise_spec) (L8,S2):(mat:4,red),S2:forest|brush