1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
23 from mediagoblin
.tools
.exif
import exif_fix_image_orientation
, \
24 extract_exif
, clean_exif
, get_gps_data
, get_useful
25 from .resources
import GOOD_JPG
, EMPTY_JPG
, BAD_JPG
, GPS_JPG
29 assert a
in b
, "%r not in %r" % (a
, b
)
32 def test_exif_extraction():
34 Test EXIF extraction from a good image
36 result
= extract_exif(GOOD_JPG
)
37 clean
= clean_exif(result
)
38 useful
= get_useful(clean
)
39 gps
= get_gps_data(result
)
41 # Do we have the result?
42 assert len(result
) == 55
44 # Do we have clean data?
45 assert len(clean
) == 53
50 # Do we have the "useful" tags?
51 assert useful
== {'EXIF CVAPattern': {'field_length': 8,
52 'field_offset': 26224,
54 'printable': u
'[0, 2, 0, 2, 1, 2, 0, 1]',
56 'values': [0, 2, 0, 2, 1, 2, 0, 1]},
57 'EXIF ColorSpace': {'field_length': 2,
63 'EXIF ComponentsConfiguration': {'field_length': 4,
66 'printable': u
'YCbCr',
68 'values': [1, 2, 3, 0]},
69 'EXIF CompressedBitsPerPixel': {'field_length': 8,
75 'EXIF Contrast': {'field_length': 2,
81 'EXIF CustomRendered': {'field_length': 2,
84 'printable': u
'Normal',
87 'EXIF DateTimeDigitized': {'field_length': 20,
90 'printable': u
'2011:06:22 12:20:33',
92 'values': u
'2011:06:22 12:20:33'},
93 'EXIF DateTimeOriginal': {'field_length': 20,
96 'printable': u
'2011:06:22 12:20:33',
98 'values': u
'2011:06:22 12:20:33'},
99 'EXIF DigitalZoomRatio': {'field_length': 8,
100 'field_offset': 26232,
105 'EXIF ExifImageLength': {'field_length': 2,
108 'printable': u
'2592',
111 'EXIF ExifImageWidth': {'field_length': 2,
114 'printable': u
'3872',
117 'EXIF ExifVersion': {'field_length': 4,
120 'printable': u
'0221',
122 'values': [48, 50, 50, 49]},
123 'EXIF ExposureBiasValue': {'field_length': 8,
129 'EXIF ExposureMode': {'field_length': 2,
132 'printable': u
'Manual Exposure',
135 'EXIF ExposureProgram': {'field_length': 2,
138 'printable': u
'Manual',
141 'EXIF ExposureTime': {'field_length': 8,
144 'printable': u
'1/125',
146 'values': [[1, 125]]},
147 'EXIF FNumber': {'field_length': 8,
152 'values': [[10, 1]]},
153 'EXIF FileSource': {'field_length': 1,
156 'printable': u
'Digital Camera',
159 'EXIF Flash': {'field_length': 2,
162 'printable': u
'Flash did not fire',
165 'EXIF FlashPixVersion': {'field_length': 4,
168 'printable': u
'0100',
170 'values': [48, 49, 48, 48]},
171 'EXIF FocalLength': {'field_length': 8,
176 'values': [[18, 1]]},
177 'EXIF FocalLengthIn35mmFilm': {'field_length': 2,
183 'EXIF GainControl': {'field_length': 2,
186 'printable': u
'None',
189 'EXIF ISOSpeedRatings': {'field_length': 2,
195 'EXIF InteroperabilityOffset': {'field_length': 4,
198 'printable': u
'26240',
201 'EXIF LightSource': {'field_length': 2,
204 'printable': u
'Unknown',
207 'EXIF MaxApertureValue': {'field_length': 8,
210 'printable': u
'18/5',
212 'values': [[18, 5]]},
213 'EXIF MeteringMode': {'field_length': 2,
216 'printable': u
'Pattern',
219 'EXIF Saturation': {'field_length': 2,
222 'printable': u
'Normal',
225 'EXIF SceneCaptureType': {'field_length': 2,
228 'printable': u
'Standard',
231 'EXIF SceneType': {'field_length': 1,
234 'printable': u
'Directly Photographed',
237 'EXIF SensingMethod': {'field_length': 2,
240 'printable': u
'One-chip color area',
243 'EXIF Sharpness': {'field_length': 2,
246 'printable': u
'Normal',
249 'EXIF SubSecTime': {'field_length': 3,
255 'EXIF SubSecTimeDigitized': {'field_length': 3,
261 'EXIF SubSecTimeOriginal': {'field_length': 3,
267 'EXIF SubjectDistanceRange': {'field_length': 2,
273 'EXIF WhiteBalance': {'field_length': 2,
276 'printable': u
'Auto',
279 'Image DateTime': {'field_length': 20,
282 'printable': u
'2011:06:22 12:20:33',
284 'values': u
'2011:06:22 12:20:33'},
285 'Image ExifOffset': {'field_length': 4,
291 'Image Make': {'field_length': 18,
294 'printable': u
'NIKON CORPORATION',
296 'values': u
'NIKON CORPORATION'},
297 'Image Model': {'field_length': 10,
300 'printable': u
'NIKON D80',
302 'values': u
'NIKON D80'},
303 'Image Orientation': {'field_length': 2,
306 'printable': u
'Rotated 90 CCW',
309 'Image ResolutionUnit': {'field_length': 2,
312 'printable': u
'Pixels/Inch',
315 'Image Software': {'field_length': 15,
318 'printable': u
'Shotwell 0.9.3',
320 'values': u
'Shotwell 0.9.3'},
321 'Image XResolution': {'field_length': 8,
326 'values': [[300, 1]]},
327 'Image YCbCrPositioning': {'field_length': 2,
330 'printable': u
'Co-sited',
333 'Image YResolution': {'field_length': 8,
338 'values': [[300, 1]]},
339 'Thumbnail Compression': {'field_length': 2,
340 'field_offset': 26280,
342 'printable': u
'JPEG (old-style)',
345 'Thumbnail ResolutionUnit': {'field_length': 2,
346 'field_offset': 26316,
348 'printable': u
'Pixels/Inch',
351 'Thumbnail XResolution': {'field_length': 8,
352 'field_offset': 26360,
356 'values': [[300, 1]]},
357 'Thumbnail YCbCrPositioning': {'field_length': 2,
358 'field_offset': 26352,
360 'printable': u
'Co-sited',
363 'Thumbnail YResolution': {'field_length': 8,
364 'field_offset': 26368,
368 'values': [[300, 1]]}}
371 def test_exif_image_orientation():
373 Test image reorientation based on EXIF data
375 result
= extract_exif(GOOD_JPG
)
377 image
= exif_fix_image_orientation(
378 Image
.open(GOOD_JPG
),
381 # Are the dimensions correct?
382 assert image
.size
== (428, 640)
384 # If this pixel looks right, the rest of the image probably will too.
385 assert_in(image
.getdata()[10000],
386 ((41, 28, 11), (43, 27, 11))
390 def test_exif_no_exif():
392 Test an image without exif
394 result
= extract_exif(EMPTY_JPG
)
395 clean
= clean_exif(result
)
396 useful
= get_useful(clean
)
397 gps
= get_gps_data(result
)
405 def test_exif_bad_image():
407 Test EXIF extraction from a faithful, but bad image
409 result
= extract_exif(BAD_JPG
)
410 clean
= clean_exif(result
)
411 useful
= get_useful(clean
)
412 gps
= get_gps_data(result
)
420 def test_exif_gps_data():
422 Test extractiion of GPS data
424 result
= extract_exif(GPS_JPG
)
425 gps
= get_gps_data(result
)
428 'latitude': 59.336666666666666,
429 'direction': 25.674046740467404,
430 'altitude': 37.64365671641791,
431 'longitude': 18.016166666666667}