def white_point(x_value=None, y_value=None): """ Returns the MIX WhitePoint element. :x_value: The X value of white point chromaticity as a list :y_value: The Y value of white point chromaticity as a list Returns the following ElementTree structure:: <mix:WhitePoint> <mix:whitePointXValue> <mix:numerator>10</mix:numerator> <mix:denominator>1</mix:denominator> </mix:whitePointXValue> <mix:whitePointYValue> <mix:numerator>10</mix:numerator> <mix:denominator>1</mix:denominator> </mix:whitePointYValue> </mix:WhitePoint> """ container = _element('WhitePoint') if x_value: _rationaltype_element('whitePointXValue', x_value, parent=container) if y_value: _rationaltype_element('whitePointYValue', y_value, parent=container) return container
def spatial_metrics(plane=None, unit=None, x_sampling=None, y_sampling=None): """ Returns the MIX SpatialMetrics element. :plane: The sampling frequency plane as a string :unit: The sampling frequency unit as a string :x_sampling: The y sampling frequency as a list (or integer) :y_sampling: The x sampling frequency as a list (or integer) Returns the following ElementTree structure:: <mix:SpatialMetrics> <mix:samplingFrequencyPlane> object plane </mix:samplingFrequencyPlane> <mix:samplingFrequencyUnit>cm</mix:samplingFrequencyUnit> <mix:xSamplingFrequency> <mix:numerator>10</mix:numerator> <mix:denominator>1</mix:denominator> </mix:xSamplingFrequency> <mix:ySamplingFrequency> <mix:numerator>10</mix:numerator> <mix:denominator>1</mix:denominator> </mix:ySamplingFrequency> </mix:SpatialMetrics> """ container = _element('SpatialMetrics') if plane: plane_el = _subelement(container, 'samplingFrequencyPlane') if plane in SAMPLING_FREQUENCY_PLANES: plane_el.text = plane else: raise RestrictedElementError(plane, 'samplingFrequencyPlane', SAMPLING_FREQUENCY_PLANES) if unit: unit_el = _subelement(container, 'samplingFrequencyUnit') if unit in SAMPLING_FREQUENCY_UNITS: unit_el.text = unit else: raise RestrictedElementError(unit, 'samplingFrequencyUnit', SAMPLING_FREQUENCY_UNITS) if x_sampling: _rationaltype_element('xSamplingFrequency', x_sampling, parent=container) if y_sampling: _rationaltype_element('ySamplingFrequency', y_sampling, parent=container) return container
def compression(compression_scheme=None, local_list=None, local_value=None, compression_ratio=None): """ Returns the MIX Compression element. :compression_scheme: The compression scheme as a string :local_list: The location of the local enumerated list of compression schemes as a string :local_value: The local compression scheme as a string :compression_ratio: The compression ratio as a list (or integer) Returns the following ElementTree structure:: <mix:Compression> <mix:compressionScheme> JPEG 2000 Lossless </mix:compressionScheme> <mix:compressionSchemeLocalList> </mix:compressionSchemeLocalList> <mix:compressionSchemeLocalValue> </mix:compressionSchemeLocalValue> <mix:compressionRatio> <mix:numerator>10</mix:numerator> <mix:denominator>1</mix:denominator> </mix:compressionRatio> </mix:Compression> """ container = _element('Compression') if compression_scheme: compression_scheme_el = _subelement(container, 'compressionScheme') compression_scheme_el.text = compression_scheme if compression_scheme == 'enumerated in local list': local_list_el = _subelement(container, 'compressionSchemeLocalList') local_list_el.text = local_list local_value_el = _subelement(container, 'compressionSchemeLocalValue') local_value_el.text = local_value if compression_ratio: _rationaltype_element('compressionRatio', compression_ratio, parent=container) return container
def component(c_photometric_interpretation=None, footroom=None, headroom=None): """ Returns MIX Component element. :c_photometric_interpretation: The component photometric interpretation type as a string :footroom: The footroom as a list (or integer) :headroom: The headroom as a list (or integer) Returns the following ElementTree structure:: <mix:Component> <mix:componentPhotometricInterpretation> R </mix:componentPhotometricInterpretation> <mix:footroom> <mix:numerator>10</mix:numerator> <mix:denominator>1</mix:denominator> </mix:footroom> <mix:headroom> <mix:numerator>20</mix:numerator> <mix:denominator>1</mix:denominator> </mix:headroom> </mix:Component> """ container = _element('Component') if c_photometric_interpretation: if c_photometric_interpretation in COMPONENT_INTERPRETATION_TYPES: cpi_el = _subelement(container, 'componentPhotometricInterpretation') cpi_el.text = c_photometric_interpretation else: raise RestrictedElementError(c_photometric_interpretation, 'componentPhotometricInterpretation', COMPONENT_INTERPRETATION_TYPES) if footroom: _rationaltype_element('footroom', footroom, parent=container) if headroom: _rationaltype_element('headroom', headroom, parent=container) return container
def test_rationaltype_element(): """ Tests the _rationaltype_element function by asserting that the element was created correctly, that it is containing both the numerator and denominator subelements even though the numerator sometimes is missing or is None. Also assert that the rational element is a subelement of its parent element if parent argument is given. """ elem1 = _rationaltype_element('test', 30) assert ET.tostring(elem1) == ET.tostring(ET.fromstring( '<mix:test xmlns:mix="http://www.loc.gov/mix/v20">' '<mix:numerator>30</mix:numerator>' '<mix:denominator>1</mix:denominator></mix:test>')) elem2 = _rationaltype_element('test', [30, 3]) assert ET.tostring(elem2) == ET.tostring(ET.fromstring( '<mix:test xmlns:mix="http://www.loc.gov/mix/v20">' '<mix:numerator>30</mix:numerator>' '<mix:denominator>3</mix:denominator></mix:test>')) elem3 = _rationaltype_element('test', [30, None]) assert ET.tostring(elem3) == ET.tostring(ET.fromstring( '<mix:test xmlns:mix="http://www.loc.gov/mix/v20">' '<mix:numerator>30</mix:numerator>' '<mix:denominator>1</mix:denominator></mix:test>')) elem4 = _rationaltype_element('test', [30, '']) assert ET.tostring(elem4) == ET.tostring(ET.fromstring( '<mix:test xmlns:mix="http://www.loc.gov/mix/v20">' '<mix:numerator>30</mix:numerator>' '<mix:denominator>1</mix:denominator></mix:test>')) parent = _element('parent') elem5 = _rationaltype_element('test', [30], parent=parent) assert ET.tostring(elem5) == ET.tostring(ET.fromstring( '<mix:test xmlns:mix="http://www.loc.gov/mix/v20">' '<mix:numerator>30</mix:numerator>' '<mix:denominator>1</mix:denominator></mix:test>')) assert elem5.getparent().tag == '{http://www.loc.gov/mix/v20}parent'
def _gps_group(tag, degrees=None, minutes=None, seconds=None): """ Returns the MIX gpsGroup type element. :tag: the tag name of the container element :degrees: The degrees of the coordinates as a list (or integer) :minutes: The minutes of the coordinates as a list (or integer) :seconds: The seconds of the coordinates as a list (or integer) Returns the following ElementTree structure:: <mix:{{ tag }}> <mix:degrees> <mix:numerator>2</mix:numerator> <mix:denominator>1</mix:denominator> </mix:degrees> <mix:minutes> <mix:numerator>2</mix:numerator> <mix:denominator>1</mix:denominator> </mix:minutes> <mix:seconds> <mix:numerator>2</mix:numerator> <mix:denominator>1</mix:denominator> </mix:seconds> </mix:{{ tag }}> """ container = _element(tag) if degrees: degrees_el = _rationaltype_element('degrees', degrees) container.append(degrees_el) if minutes: minutes_el = _rationaltype_element('minutes', minutes) container.append(minutes_el) if seconds: seconds_el = _rationaltype_element('seconds', seconds) container.append(seconds_el) return container
def ycbcr(subsample_horiz=None, subsample_vert=None, positioning=None, luma_red=None, luma_green=None, luma_blue=None): """ Returns the MIX YCbCr element and its subelements. :subsample_horiz: The horizontal subsample factor as a string :subsample_vert: The vertical subsample factor as a string :positioning: The positions of subsamples as a string :luma_red: The red luminance value as a list (or integer) :luma_green: The green luminane value as a list (or integer) :luma_blue: The blue luminance value as a list (or integer) Returns the following sorted ElementTree structure:: <mix:YCbCr> <mix:YCbCrSubSampling> <mix:yCbCrSubsampleHoriz>1</mix:yCbCrSubsampleHoriz> <mix:yCbCrSubsampleVert>2</mix:yCbCrSubsampleVert> </mix:YCbCrSubSampling/> <mix:yCbCrPositioning>1</mix:yCbCrPositioning> <mix:YCbCrCoefficients> <mix:lumaRed> <mix:numerator>10</mix:numerator> <mix:denominator>1</mix:denominator> </mix:lumaRed> <mix:lumaGreen> <mix:numerator>20</mix:numerator> <mix:denominator>1</mix:denominator> </mix:lumaGreen> <mix:lumaBlue> <mix:numerator>30</mix:numerator> <mix:denominator>1</mix:denominator> </mix:lumaBlue> </mix:YCbCrCoefficients/> </mix:YCbCr> """ container = _element('YCbCr') if subsample_horiz or subsample_vert: subsample_container = _subelement(container, 'YCbCrSubSampling') if subsample_horiz: if subsample_horiz in YCBCR_SUBSAMPLE_TYPES: subsample_horiz_el = _subelement(subsample_container, 'yCbCrSubsampleHoriz') subsample_horiz_el.text = subsample_horiz else: raise RestrictedElementError(subsample_horiz, 'yCbCrSubsampleHoriz', YCBCR_SUBSAMPLE_TYPES) if subsample_vert: if subsample_vert in YCBCR_SUBSAMPLE_TYPES: subsample_vert_el = _subelement(subsample_container, 'yCbCrSubsampleVert') subsample_vert_el.text = subsample_vert else: raise RestrictedElementError(subsample_vert, 'yCbCrSubsampleVert', YCBCR_SUBSAMPLE_TYPES) if positioning: if positioning in YCBCR_POSITIONING_TYPES: positioning_el = _subelement(container, 'yCbCrPositioning') positioning_el.text = positioning else: raise RestrictedElementError(positioning, 'yCbCrPositioning', YCBCR_POSITIONING_TYPES) if luma_red or luma_green or luma_blue: luma_container = _subelement(container, 'YCbCrCoefficients') if luma_red: _rationaltype_element('lumaRed', luma_red, parent=luma_container) if luma_green: _rationaltype_element('lumaGreen', luma_green, parent=luma_container) if luma_blue: _rationaltype_element('lumaBlue', luma_blue, parent=luma_container) return container
def primary_chromaticities(red_x=None, red_y=None, green_x=None, green_y=None, blue_x=None, blue_y=None): """ Returns the MIX PrimaryChromaticities element. :red_x: The red X value for the chromaticities as a list :red_x: The red Y value for the chromaticities as a list :red_x: The green X value for the chromaticities as a list :red_x: The green Y value for the chromaticities as a list :red_x: The blue X value for the chromaticities as a list :red_x: The blue Y value for the chromaticities as a list Returns the following ElementTree structure:: <mix:PrimaryChromaticities> <mix:primaryChromaticitiesRedX> <mix:numerator>10</mix:numerator> <mix:denominator>1</mix:denominator> </mix:primaryChromaticitiesRedX> <mix:primaryChromaticitiesRedY> <mix:numerator>10</mix:numerator> <mix:denominator>1</mix:denominator> </mix:primaryChromaticitiesRedY> <mix:primaryChromaticitiesGreenX> <mix:numerator>10</mix:numerator> <mix:denominator>1</mix:denominator> </mix:primaryChromaticitiesGreenX> <mix:primaryChromaticitiesGreenY> <mix:numerator>10</mix:numerator> <mix:denominator>1</mix:denominator> </mix:primaryChromaticitiesGreenY> <mix:primaryChromaticitiesBlueX> <mix:numerator>10</mix:numerator> <mix:denominator>1</mix:denominator> </mix:primaryChromaticitiesBlueX> <mix:primaryChromaticitiesBlueY> <mix:numerator>10</mix:numerator> <mix:denominator>1</mix:denominator> </mix:primaryChromaticitiesBlueY> </mix:PrimaryChromaticities> """ container = _element('PrimaryChromaticities') if red_x: _rationaltype_element('primaryChromaticitiesRedX', red_x, parent=container) if red_y: _rationaltype_element('primaryChromaticitiesRedY', red_y, parent=container) if green_x: _rationaltype_element('primaryChromaticitiesGreenX', green_x, parent=container) if green_y: _rationaltype_element('primaryChromaticitiesGreenY', green_y, parent=container) if blue_x: _rationaltype_element('primaryChromaticitiesBlueX', blue_x, parent=container) if blue_y: _rationaltype_element('primaryChromaticitiesBlueY', blue_y, parent=container) return container
def gps_data(contents=None): """ Returns the MIX GPSData element. The function argument contents is a dict, from nisomix.GPS_DATA_CONTENTS. The keys from the contents dict are matched to create the MIX element and its substructure. The dict should look like this:: contents = {"version_id": None, "lat_ref": None, "lat_degrees": None, "lat_minutes": None, "lat_seconds": None, "long_ref": None, "long_degrees": None, "long_minutes": None, "long_seconds": None, "altitude_ref": None, "altitude": None, "timestamp": None, "satellites": None, "status": None, "measure_mode": None, "dop": None, "speed_ref": None, "speed": None, "track_ref": None, "track": None, "img_direction_ref": None, "direction": None, "map_datum": None, "dest_lat_ref": None, "dest_lat_degrees": None, "dest_lat_minutes": None, "dest_lat_seconds": None, "dest_long_ref": None, "dest_long_degrees": None, "dest_long_minutes": None, "dest_long_seconds": None, "dest_bearing_ref": None, "dest_bearing": None, "dest_distance_ref": None, "dest_distance": None, "processing_method": None, "area_information": None, "datestamp": None, "differential": None, "gps_groups": None} """ tags = { 'version_id': 'gpsVersionID', 'lat_ref': 'gpsLatitudeRef', 'long_ref': 'gpsLongitudeRef', 'altitude_ref': 'gpsAltitudeRef', 'timestamp': 'gpsTimeStamp', 'satellites': 'gpsSatellites', 'status': 'gpsStatus', 'measure_mode': 'gpsMeasureMode', 'speed_ref': 'gpsSpeedRef', 'track_ref': 'gpsTrackRef', 'img_direction_ref': 'gpsImgDirectionRef', 'map_datum': 'gpsMapDatum', 'dest_lat_ref': 'gpsDestLatitudeRef', 'dest_long_ref': 'gpsDestLongitudeRef', 'dest_bearing_ref': 'gpsDestBearingRef', 'dest_distance_ref': 'gpsDestDistanceRef', 'processing_method': 'gpsProcessingMethod', 'area_information': 'gpsAreaInformation', 'datestamp': 'gpsDateStamp', 'differential': 'gpsDifferential' } rationals = { 'altitude': 'gpsAltitude', 'dop': 'gpsDOP', 'speed': 'gpsSpeed', 'track': 'gpsTrack', 'direction': 'gpsImgDirection', 'dest_bearing': 'gpsDestBearing', 'dest_distance': 'gpsDestDistance' } for key in contents: if key not in GPS_DATA_CONTENTS: raise ValueError('Key "%s" not in supported keys ' 'for gps_data.' % key) container = _element('GPSData') child_elements = [] for key, value in six.iteritems(contents): if key in tags and value: elem = _element(tags[key]) elem.text = value child_elements.append(elem) if key in rationals and value: elem = _rationaltype_element(rationals[key], value) child_elements.append(elem) if contents.get("lat_degrees") or contents.get("lat_minutes") or \ contents.get("lat_seconds"): lat_group = _gps_group('GPSLatitude', degrees=contents["lat_degrees"], minutes=contents["lat_minutes"], seconds=contents["lat_seconds"]) child_elements.append(lat_group) if contents.get("long_degrees") or contents.get("long_minutes") or \ contents.get("long_seconds"): long_group = _gps_group('GPSLongitude', degrees=contents["long_degrees"], minutes=contents["long_minutes"], seconds=contents["long_seconds"]) child_elements.append(long_group) if contents.get("dest_lat_degrees") or \ contents.get("dest_lat_minutes") or \ contents.get("dest_lat_seconds"): dest_lat_group = _gps_group('GPSDestLatitude', degrees=contents["dest_lat_degrees"], minutes=contents["dest_lat_minutes"], seconds=contents["dest_lat_seconds"]) child_elements.append(dest_lat_group) if contents.get("dest_long_degrees") or \ contents.get("dest_long_minutes") or \ contents.get("dest_long_seconds"): dest_long_group = _gps_group('GPSDestLongitude', degrees=contents["dest_long_degrees"], minutes=contents["dest_long_minutes"], seconds=contents["dest_long_seconds"]) child_elements.append(dest_long_group) child_elements.sort(key=gps_data_order) for element in child_elements: container.append(element) return container
def image_data(contents=None): """ Returns the MIX ImageData element. The function argument contents is a dict, that can be retrieved from nisomix.IMAGE_DATA_CONTENTS. The keys from contents are matched to create the MIX element and its substructure. The dict should look like this:: contents = {"fnumber": None, "exposure_time": None, "exposure_program": None, "spectral_sensitivity": None, "isospeed_ratings": None, "oecf": None, "exif_version": None, "shutter_speed_value": None, "aperture_value": None, "brightness_value": None, "exposure_bias_value": None, "max_aperture_value": None, "distance": None, "min_distance": None, "max_distance": None, "metering_mode": None, "light_source": None, "flash": None, "focal_length": None, "flash_energy": None, "back_light": None, "exposure_index": None, "sensing_method": None, "cfa_pattern": None, "auto_focus": None, "x_print_aspect_ratio": None, "y_print_aspect_ratio": None} """ tags = { 'fnumber': 'fNumber', 'exposure_time': 'exposureTime', 'exposure_program': 'exposureProgram', 'isospeed_ratings': 'isoSpeedRatings', 'exif_version': 'exifVersion', 'metering_mode': 'meteringMode', 'light_source': 'lightSource', 'flash': 'flash', 'focal_length': 'focalLength', 'back_light': 'backLight', 'exposure_index': 'exposureIndex', 'sensing_method': 'sensingMethod', 'cfa_pattern': 'cfaPattern', 'auto_focus': 'autoFocus' } rationals = { 'oecf': 'oECF', 'shutter_speed_value': 'shutterSpeedValue', 'aperture_value': 'apertureValue', 'brightness_value': 'brightnessValue', 'exposure_bias_value': 'exposureBiasValue', 'max_aperture_value': 'maxApertureValue', 'flash_energy': 'flashEnergy' } for key in contents: if key not in IMAGE_DATA_CONTENTS: raise ValueError('Key "%s" not in supported keys for ' 'image_data.' % key) container = _element('ImageData') child_elements = [] for key, value in six.iteritems(contents): if key in tags and value: elem = _element(tags[key]) elem.text = six.text_type(value) child_elements.append(elem) if key in rationals and value: elem = _rationaltype_element(rationals[key], value) child_elements.append(elem) if contents.get("spectral_sensitivity"): spect_sens = _ensure_list(contents["spectral_sensitivity"]) for item in spect_sens: spect_sens_el = _element('spectralSensitivity') spect_sens_el.text = item child_elements.append(spect_sens_el) if contents.get("distance") or contents.get("min_distance") \ or contents.get("max_distance"): subject_distance = _element('SubjectDistance') child_elements.append(subject_distance) if contents.get("distance"): distance_el = _subelement(subject_distance, 'distance') distance_el.text = contents["distance"] if contents.get("min_distance") or contents.get("max_distance"): min_max_distance = _subelement(subject_distance, 'MinMaxDistance') if contents.get("min_distance"): min_distance_el = _subelement(min_max_distance, 'minDistance') min_distance_el.text = contents["min_distance"] if contents.get("max_distance"): max_distance_el = _subelement(min_max_distance, 'maxDistance') max_distance_el.text = contents["max_distance"] if contents.get("x_print_aspect_ratio") or \ contents.get("y_print_aspect_ratio"): print_ratio = _element('PrintAspectRatio') child_elements.append(print_ratio) if contents.get("x_print_aspect_ratio"): x_print_aspect_ratio_el = _subelement(print_ratio, 'xPrintAspectRatio') x_print_aspect_ratio_el.text = contents["x_print_aspect_ratio"] if contents.get("y_print_aspect_ratio"): y_print_aspect_ratio_el = _subelement(print_ratio, 'yPrintAspectRatio') y_print_aspect_ratio_el.text = contents["y_print_aspect_ratio"] child_elements.sort(key=image_data_order) for element in child_elements: container.append(element) return container