-
Notifications
You must be signed in to change notification settings - Fork 7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[fbsync] Added support for EXIF orientation transform in read_image f…
…or JPEG (#8279) Reviewed By: vmoens Differential Revision: D55062774 fbshipit-source-id: 6a75321d8f86630d54cd15ad7433f71d0735659d
- Loading branch information
1 parent
87967f4
commit 7e4fbf7
Showing
8 changed files
with
302 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
/*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 <jpeglib.h> | ||
#include <torch/types.h> | ||
#include <vector> | ||
|
||
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<unsigned char>& 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<unsigned char>& 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<unsigned char>& 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<unsigned char> 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.