/*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, installing or using the software you agree to this license. // If you do not agree to this license, do not download, install, // copy or use the software. // // // License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. // Copyright (C) 2009, Willow Garage Inc., all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistribution's of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistribution's in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * The name of the copyright holders may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and // any express or implied warranties, including, but not limited to, the implied // warranties of merchantability and fitness for a particular purpose are disclaimed. // In no event shall the Intel Corporation or contributors be liable for any direct, // indirect, incidental, special, exemplary, or consequential damages // (including, but not limited to, procurement of substitute goods or services; // loss of use, data, or profits; or business interruption) however caused // and on any theory of liability, whether in contract, strict liability, // or tort (including negligence or otherwise) arising in any way out of // the use of this software, even if advised of the possibility of such damage. // //M*/ #pragma once // Functions in this module are taken from OpenCV // https://github.com/opencv/opencv/blob/097891e311fae1d8354eb092a0fd0171e630d78c/modules/imgcodecs/src/exif.cpp #include #include #include namespace vision { namespace image { namespace exif_private { constexpr uint16_t APP1 = 0xe1; constexpr uint16_t ENDIANNESS_INTEL = 0x49; constexpr uint16_t ENDIANNESS_MOTO = 0x4d; constexpr uint16_t REQ_EXIF_TAG_MARK = 0x2a; constexpr uint16_t ORIENTATION_EXIF_TAG = 0x0112; constexpr uint16_t INCORRECT_TAG = -1; inline uint16_t get_endianness(const std::vector& exif_data) { if ((exif_data.size() < 1) || (exif_data.size() > 1 && exif_data[0] != exif_data[1])) { return 0; } if (exif_data[0] == 'I') { return ENDIANNESS_INTEL; } if (exif_data[0] == 'M') { return ENDIANNESS_MOTO; } return 0; } inline uint16_t get_uint16( const std::vector& exif_data, uint16_t endianness, const size_t offset) { if (offset + 1 >= exif_data.size()) { return INCORRECT_TAG; } if (endianness == ENDIANNESS_INTEL) { return exif_data[offset] + (exif_data[offset + 1] << 8); } return (exif_data[offset] << 8) + exif_data[offset + 1]; } inline uint32_t get_uint32( const std::vector& exif_data, uint16_t endianness, const size_t offset) { if (offset + 3 >= exif_data.size()) { return INCORRECT_TAG; } if (endianness == ENDIANNESS_INTEL) { return exif_data[offset] + (exif_data[offset + 1] << 8) + (exif_data[offset + 2] << 16) + (exif_data[offset + 3] << 24); } return (exif_data[offset] << 24) + (exif_data[offset + 1] << 16) + (exif_data[offset + 2] << 8) + exif_data[offset + 3]; } inline int fetch_exif_orientation(j_decompress_ptr cinfo) { int exif_orientation = -1; // Check for Exif marker APP1 jpeg_saved_marker_ptr exif_marker = 0; jpeg_saved_marker_ptr cmarker = cinfo->marker_list; while (cmarker && exif_marker == 0) { if (cmarker->marker == APP1) { exif_marker = cmarker; } cmarker = cmarker->next; } if (exif_marker) { // Exif binary structure looks like this // First 6 bytes: [E, x, i, f, 0, 0] // Endianness, 2 bytes : [M, M] or [I, I] // Tag mark, 2 bytes: [0, 0x2a] // Offset, 4 bytes // Num entries, 2 bytes // Tag entries and data, tag has 2 bytes and its data has 10 bytes // For more details: // http://www.media.mit.edu/pia/Research/deepview/exif.html // Bytes from Exif size field to the first TIFF header constexpr size_t start_offset = 6; if (exif_marker->data_length > start_offset) { auto* exif_data_ptr = exif_marker->data + start_offset; auto size = exif_marker->data_length - start_offset; // Here we copy the data into the vector structure // TODO: we can avoid copying the data and read directly from the pointer std::vector exif_data_vec( exif_data_ptr, exif_data_ptr + size); auto endianness = get_endianness(exif_data_vec); // Checking whether Tag Mark (0x002A) correspond to one contained in the // Jpeg file uint16_t tag_mark = get_uint16(exif_data_vec, endianness, 2); if (tag_mark == REQ_EXIF_TAG_MARK) { auto offset = get_uint32(exif_data_vec, endianness, 4); size_t num_entry = get_uint16(exif_data_vec, endianness, offset); offset += 2; // go to start of tag fields constexpr size_t tiff_field_size = 12; for (size_t entry = 0; entry < num_entry; entry++) { // Here we just search for orientation tag and parse it auto tag_num = get_uint16(exif_data_vec, endianness, offset); if (tag_num == INCORRECT_TAG) { break; } if (tag_num == ORIENTATION_EXIF_TAG) { exif_orientation = get_uint16(exif_data_vec, endianness, offset + 8); break; } offset += tiff_field_size; } } } } return exif_orientation; } constexpr uint16_t IMAGE_ORIENTATION_TL = 1; // normal orientation constexpr uint16_t IMAGE_ORIENTATION_TR = 2; // needs horizontal flip constexpr uint16_t IMAGE_ORIENTATION_BR = 3; // needs 180 rotation constexpr uint16_t IMAGE_ORIENTATION_BL = 4; // needs vertical flip constexpr uint16_t IMAGE_ORIENTATION_LT = 5; // mirrored horizontal & rotate 270 CW constexpr uint16_t IMAGE_ORIENTATION_RT = 6; // rotate 90 CW constexpr uint16_t IMAGE_ORIENTATION_RB = 7; // mirrored horizontal & rotate 90 CW constexpr uint16_t IMAGE_ORIENTATION_LB = 8; // needs 270 CW rotation inline torch::Tensor exif_orientation_transform( const torch::Tensor& image, int orientation) { if (orientation == IMAGE_ORIENTATION_TL) { return image; } else if (orientation == IMAGE_ORIENTATION_TR) { return image.flip(-1); } else if (orientation == IMAGE_ORIENTATION_BR) { // needs 180 rotation equivalent to // flip both horizontally and vertically return image.flip({-2, -1}); } else if (orientation == IMAGE_ORIENTATION_BL) { return image.flip(-2); } else if (orientation == IMAGE_ORIENTATION_LT) { return image.transpose(-1, -2); } else if (orientation == IMAGE_ORIENTATION_RT) { return image.transpose(-1, -2).flip(-1); } else if (orientation == IMAGE_ORIENTATION_RB) { return image.transpose(-1, -2).flip({-2, -1}); } else if (orientation == IMAGE_ORIENTATION_LB) { return image.transpose(-1, -2).flip(-2); } return image; } } // namespace exif_private } // namespace image } // namespace vision