Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
O
opencv
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
opencv
Commits
0a306f88
Commit
0a306f88
authored
Oct 11, 2015
by
micalan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Camera orientation handling is added for jpeg files
parent
d430e802
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
460 additions
and
0 deletions
+460
-0
CMakeLists.txt
modules/imgcodecs/CMakeLists.txt
+2
-0
grfmt_jpeg.cpp
modules/imgcodecs/src/grfmt_jpeg.cpp
+56
-0
grfmt_jpeg.hpp
modules/imgcodecs/src/grfmt_jpeg.hpp
+6
-0
jpeg_exif.cpp
modules/imgcodecs/src/jpeg_exif.cpp
+0
-0
jpeg_exif.hpp
modules/imgcodecs/src/jpeg_exif.hpp
+251
-0
test_grfmt.cpp
modules/imgcodecs/test/test_grfmt.cpp
+145
-0
No files found.
modules/imgcodecs/CMakeLists.txt
View file @
0a306f88
...
...
@@ -61,6 +61,8 @@ list(APPEND grfmt_hdrs ${CMAKE_CURRENT_LIST_DIR}/src/bitstrm.hpp)
list
(
APPEND grfmt_srcs
${
CMAKE_CURRENT_LIST_DIR
}
/src/bitstrm.cpp
)
list
(
APPEND grfmt_hdrs
${
CMAKE_CURRENT_LIST_DIR
}
/src/rgbe.hpp
)
list
(
APPEND grfmt_srcs
${
CMAKE_CURRENT_LIST_DIR
}
/src/rgbe.cpp
)
list
(
APPEND grfmt_hdrs
${
CMAKE_CURRENT_LIST_DIR
}
/src/jpeg_exif.hpp
)
list
(
APPEND grfmt_srcs
${
CMAKE_CURRENT_LIST_DIR
}
/src/jpeg_exif.cpp
)
source_group
(
"Src
\\
grfmts"
FILES
${
grfmt_hdrs
}
${
grfmt_srcs
}
)
...
...
modules/imgcodecs/src/grfmt_jpeg.cpp
View file @
0a306f88
...
...
@@ -41,6 +41,7 @@
#include "precomp.hpp"
#include "grfmt_jpeg.hpp"
#include "jpeg_exif.hpp"
#ifdef HAVE_JPEG
...
...
@@ -177,6 +178,7 @@ JpegDecoder::JpegDecoder()
m_state
=
0
;
m_f
=
0
;
m_buf_supported
=
true
;
m_orientation
=
JPEG_ORIENTATION_TL
;
}
...
...
@@ -253,12 +255,64 @@ bool JpegDecoder::readHeader()
}
}
m_orientation
=
getOrientation
();
if
(
!
result
)
close
();
return
result
;
}
int
JpegDecoder
::
getOrientation
()
{
int
orientation
=
JPEG_ORIENTATION_TL
;
ExifReader
reader
(
m_filename
);
if
(
reader
.
parse
()
)
{
orientation
=
reader
.
getTag
(
ORIENTATION
).
field_u16
;
//orientation is unsigned short, so check field_u16
}
return
orientation
;
}
void
JpegDecoder
::
setOrientation
(
Mat
&
img
)
{
switch
(
m_orientation
)
{
case
JPEG_ORIENTATION_TL
:
//0th row == visual top, 0th column == visual left-hand side
//do nothing, the image already has proper orientation
break
;
case
JPEG_ORIENTATION_TR
:
//0th row == visual top, 0th column == visual right-hand side
flip
(
img
,
img
,
1
);
//flip horizontally
break
;
case
JPEG_ORIENTATION_BR
:
//0th row == visual bottom, 0th column == visual right-hand side
flip
(
img
,
img
,
-
1
);
//flip both horizontally and vertically
break
;
case
JPEG_ORIENTATION_BL
:
//0th row == visual bottom, 0th column == visual left-hand side
flip
(
img
,
img
,
0
);
//flip vertically
break
;
case
JPEG_ORIENTATION_LT
:
//0th row == visual left-hand side, 0th column == visual top
transpose
(
img
,
img
);
break
;
case
JPEG_ORIENTATION_RT
:
//0th row == visual right-hand side, 0th column == visual top
transpose
(
img
,
img
);
flip
(
img
,
img
,
1
);
//flip horizontally
break
;
case
JPEG_ORIENTATION_RB
:
//0th row == visual right-hand side, 0th column == visual bottom
transpose
(
img
,
img
);
flip
(
img
,
img
,
-
1
);
//flip both horizontally and vertically
break
;
case
JPEG_ORIENTATION_LB
:
//0th row == visual left-hand side, 0th column == visual bottom
transpose
(
img
,
img
);
flip
(
img
,
img
,
0
);
//flip vertically
break
;
default
:
//by default the image read has normal (JPEG_ORIENTATION_TL) orientation
break
;
}
}
/***************************************************************************
* following code is for supporting MJPEG image files
* based on a message of Laurent Pinchart on the video4linux mailing list
...
...
@@ -472,8 +526,10 @@ bool JpegDecoder::readData( Mat& img )
icvCvt_CMYK2Gray_8u_C4C1R
(
buffer
[
0
],
0
,
data
,
0
,
cvSize
(
m_width
,
1
)
);
}
}
result
=
true
;
jpeg_finish_decompress
(
cinfo
);
setOrientation
(
img
);
}
}
...
...
modules/imgcodecs/src/grfmt_jpeg.hpp
View file @
0a306f88
...
...
@@ -70,6 +70,12 @@ protected:
FILE
*
m_f
;
void
*
m_state
;
private
:
//Support for handling exif orientation tag in Jpeg file
int
m_orientation
;
int
getOrientation
();
void
setOrientation
(
Mat
&
img
);
};
...
...
modules/imgcodecs/src/jpeg_exif.cpp
0 → 100644
View file @
0a306f88
This diff is collapsed.
Click to expand it.
modules/imgcodecs/src/jpeg_exif.hpp
0 → 100644
View file @
0a306f88
/*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*/
#ifndef _OPENCV_JPEG_EXIF_HPP_
#define _OPENCV_JPEG_EXIF_HPP_
#include <cstdio>
#include <map>
#include <utility>
#include <algorithm>
#include <stdint.h>
#include <string>
#include <vector>
namespace
cv
{
/**
* @brief Jpeg markers that can encounter in Jpeg file
*/
enum
AppMarkerTypes
{
SOI
=
0xD8
,
SOF0
=
0xC0
,
SOF2
=
0xC2
,
DHT
=
0xC4
,
DQT
=
0xDB
,
DRI
=
0xDD
,
SOS
=
0xDA
,
RST0
=
0xD0
,
RST1
=
0xD1
,
RST2
=
0xD2
,
RST3
=
0xD3
,
RST4
=
0xD4
,
RST5
=
0xD5
,
RST6
=
0xD6
,
RST7
=
0xD7
,
APP0
=
0xE0
,
APP1
=
0xE1
,
APP2
=
0xE2
,
APP3
=
0xE3
,
APP4
=
0xE4
,
APP5
=
0xE5
,
APP6
=
0xE6
,
APP7
=
0xE7
,
APP8
=
0xE8
,
APP9
=
0xE9
,
APP10
=
0xEA
,
APP11
=
0xEB
,
APP12
=
0xEC
,
APP13
=
0xED
,
APP14
=
0xEE
,
APP15
=
0xEF
,
COM
=
0xFE
,
EOI
=
0xD9
};
/**
* @brief Base Exif tags used by IFD0 (main image)
*/
enum
ExifTagName
{
IMAGE_DESCRIPTION
=
0x010E
,
///< Image Description: ASCII string
MAKE
=
0x010F
,
///< Description of manufacturer: ASCII string
MODEL
=
0x0110
,
///< Description of camera model: ASCII string
ORIENTATION
=
0x0112
,
///< Orientation of the image: unsigned short
XRESOLUTION
=
0x011A
,
///< Resolution of the image across X axis: unsigned rational
YRESOLUTION
=
0x011B
,
///< Resolution of the image across Y axis: unsigned rational
RESOLUTION_UNIT
=
0x0128
,
///< Resolution units. '1' no-unit, '2' inch, '3' centimeter
SOFTWARE
=
0x0131
,
///< Shows firmware(internal software of digicam) version number
DATE_TIME
=
0x0132
,
///< Date/Time of image was last modified
WHITE_POINT
=
0x013E
,
///< Chromaticity of white point of the image
PRIMARY_CHROMATICIES
=
0x013F
,
///< Chromaticity of the primaries of the image
Y_CB_CR_COEFFICIENTS
=
0x0211
,
///< constant to translate an image from YCbCr to RGB format
Y_CB_CR_POSITIONING
=
0x0213
,
///< Chroma sample point of subsampling pixel array
REFERENCE_BLACK_WHITE
=
0x0214
,
///< Reference value of black point/white point
COPYRIGHT
=
0x8298
,
///< Copyright information
EXIF_OFFSET
=
0x8769
,
///< Offset to Exif Sub IFD
INVALID_TAG
=
0xFFFF
///< Shows that the tag was not recognized
};
enum
Endianess_t
{
INTEL
=
0x49
,
MOTO
=
0x4D
,
NONE
=
0x00
};
typedef
std
::
pair
<
uint32_t
,
uint32_t
>
u_rational_t
;
/**
* @brief Entry which contains possible values for different exif tags
*/
struct
ExifEntry_t
{
std
::
vector
<
u_rational_t
>
field_u_rational
;
///< vector of rational fields
std
::
string
field_str
;
///< any kind of textual information
float
field_float
;
///< Currently is not used
double
field_double
;
///< Currently is not used
uint32_t
field_u32
;
///< Unsigned 32-bit value
int32_t
field_s32
;
///< Signed 32-bit value
uint16_t
tag
;
///< Tag number
uint16_t
field_u16
;
///< Unsigned 16-bit value
int16_t
field_s16
;
///< Signed 16-bit value
uint8_t
field_u8
;
///< Unsigned 8-bit value
int8_t
field_s8
;
///< Signed 8-bit value
};
/**
* @brief Picture orientation which may be taken from JPEG's EXIF
* Orientation usually matters when the picture is taken by
* smartphone or other camera with orientation sensor support
* Corresponds to EXIF 2.3 Specification
*/
enum
JpegOrientation
{
JPEG_ORIENTATION_TL
=
1
,
///< 0th row == visual top, 0th column == visual left-hand side
JPEG_ORIENTATION_TR
=
2
,
///< 0th row == visual top, 0th column == visual right-hand side
JPEG_ORIENTATION_BR
=
3
,
///< 0th row == visual bottom, 0th column == visual right-hand side
JPEG_ORIENTATION_BL
=
4
,
///< 0th row == visual bottom, 0th column == visual left-hand side
JPEG_ORIENTATION_LT
=
5
,
///< 0th row == visual left-hand side, 0th column == visual top
JPEG_ORIENTATION_RT
=
6
,
///< 0th row == visual right-hand side, 0th column == visual top
JPEG_ORIENTATION_RB
=
7
,
///< 0th row == visual right-hand side, 0th column == visual bottom
JPEG_ORIENTATION_LB
=
8
///< 0th row == visual left-hand side, 0th column == visual bottom
};
/**
* @brief Reading exif information from Jpeg file
*
* Usage example for getting the orientation of the image:
*
* @code
* ExifReader reader(fileName);
* if( reader.parse() )
* {
* int orientation = reader.getTag(Orientation).field_u16;
* }
* @endcode
*
*/
class
ExifReader
{
public
:
/**
* @brief ExifReader constructor. Constructs an object of exif reader
*
* @param [in]filename The name of file to look exif info in
*/
explicit
ExifReader
(
std
::
string
filename
);
~
ExifReader
();
/**
* @brief Parse the file with exif info
*
* @return true if parsing was successful and exif information exists in JpegReader object
*/
bool
parse
();
/**
* @brief Get tag info by tag number
*
* @param [in] tag The tag number
* @return ExifEntru_t structure. Caller has to know what tag it calls in order to extract proper field from the structure ExifEntry_t
*/
ExifEntry_t
getTag
(
const
ExifTagName
tag
);
private
:
std
::
string
m_filename
;
std
::
vector
<
unsigned
char
>
m_data
;
std
::
map
<
int
,
ExifEntry_t
>
m_exif
;
Endianess_t
m_format
;
void
parseExif
();
bool
checkTagMark
()
const
;
size_t
getFieldSize
(
FILE
*
f
)
const
;
size_t
getNumDirEntry
()
const
;
uint32_t
getStartOffset
()
const
;
uint16_t
getExifTag
(
const
size_t
offset
)
const
;
uint16_t
getU16
(
const
size_t
offset
)
const
;
uint32_t
getU32
(
const
size_t
offset
)
const
;
uint16_t
getOrientation
(
const
size_t
offset
)
const
;
uint16_t
getResolutionUnit
(
const
size_t
offset
)
const
;
uint16_t
getYCbCrPos
(
const
size_t
offset
)
const
;
Endianess_t
getFormat
()
const
;
ExifEntry_t
parseExifEntry
(
const
size_t
offset
);
u_rational_t
getURational
(
const
size_t
offset
)
const
;
std
::
map
<
int
,
ExifEntry_t
>
getExif
();
std
::
string
getString
(
const
size_t
offset
)
const
;
std
::
vector
<
u_rational_t
>
getResolution
(
const
size_t
offset
)
const
;
std
::
vector
<
u_rational_t
>
getWhitePoint
(
const
size_t
offset
)
const
;
std
::
vector
<
u_rational_t
>
getPrimaryChromaticies
(
const
size_t
offset
)
const
;
std
::
vector
<
u_rational_t
>
getYCbCrCoeffs
(
const
size_t
offset
)
const
;
std
::
vector
<
u_rational_t
>
getRefBW
(
const
size_t
offset
)
const
;
private
:
static
const
uint16_t
tagMarkRequired
=
0x2A
;
//offset to the _number-of-directory-entry_ field
static
const
size_t
offsetNumDir
=
8
;
//max size of data in tag.
//'DDDDDDDD' contains the value of that Tag. If its size is over 4bytes,
//'DDDDDDDD' contains the offset to data stored address.
static
const
size_t
maxDataSize
=
4
;
//bytes per tag field
static
const
size_t
tiffFieldSize
=
12
;
//number of primary chromaticies components
static
const
size_t
primaryChromaticiesComponents
=
6
;
//number of YCbCr coefficients in field
static
const
size_t
ycbcrCoeffs
=
3
;
//number of Reference Black&White components
static
const
size_t
refBWComponents
=
6
;
};
}
#endif
/* JPEG_EXIF_HPP_ */
modules/imgcodecs/test/test_grfmt.cpp
View file @
0a306f88
...
...
@@ -43,6 +43,7 @@
#include "test_precomp.hpp"
#include <fstream>
#include <sstream>
using
namespace
cv
;
using
namespace
std
;
...
...
@@ -118,6 +119,150 @@ TEST(Imgcodecs_imread, regression)
}
}
template
<
class
T
>
string
to_string
(
T
i
)
{
stringstream
ss
;
string
s
;
ss
<<
i
;
s
=
ss
.
str
();
return
s
;
}
/**
* Test for check whether reading exif orientation tag was processed successfully or not
* The test info is the set of 8 images named testExifRotate_{1 to 8}.jpg
* The test image is the square 10x10 points divided by four sub-squares:
* (R corresponds to Red, G to Green, B to Blue, W to white)
* --------- ---------
* | R | G | | G | R |
* |-------| - (tag 1) |-------| - (tag 2)
* | B | W | | W | B |
* --------- ---------
*
* --------- ---------
* | W | B | | B | W |
* |-------| - (tag 3) |-------| - (tag 4)
* | G | R | | R | G |
* --------- ---------
*
* --------- ---------
* | R | B | | G | W |
* |-------| - (tag 5) |-------| - (tag 6)
* | G | W | | R | B |
* --------- ---------
*
* --------- ---------
* | W | G | | B | R |
* |-------| - (tag 7) |-------| - (tag 8)
* | B | R | | W | G |
* --------- ---------
*
*
* Every image contains exif field with orientation tag (0x112)
* After reading each image the corresponding matrix must be read as
* ---------
* | R | G |
* |-------|
* | B | W |
* ---------
*
*/
class
CV_GrfmtJpegExifOrientationTest
:
public
cvtest
::
BaseTest
{
public
:
void
run
(
int
)
{
try
{
for
(
int
i
=
1
;
i
<=
8
;
++
i
)
{
string
fileName
=
"readwrite/testExifOrientation_"
+
to_string
(
i
)
+
".jpg"
;
m_img
=
imread
(
string
(
ts
->
get_data_path
())
+
fileName
);
if
(
!
m_img
.
data
)
{
ts
->
set_failed_test_info
(
cvtest
::
TS
::
FAIL_MISSING_TEST_DATA
);
}
ts
->
printf
(
cvtest
::
TS
::
LOG
,
"start reading image
\t
%s
\n
"
,
fileName
.
c_str
());
if
(
!
checkOrientation
()
)
{
ts
->
set_failed_test_info
(
cvtest
::
TS
::
FAIL_MISMATCH
);
}
}
}
catch
(...)
{
ts
->
set_failed_test_info
(
cvtest
::
TS
::
FAIL_EXCEPTION
);
}
}
private
:
bool
checkOrientation
();
Mat
m_img
;
};
bool
CV_GrfmtJpegExifOrientationTest
::
checkOrientation
()
{
Vec3b
vec
;
int
red
=
0
;
int
green
=
0
;
int
blue
=
0
;
const
int
colorThresholdHigh
=
250
;
const
int
colorThresholdLow
=
5
;
//Checking the first quadrant (with supposed red)
vec
=
m_img
.
at
<
Vec3b
>
(
2
,
2
);
//some point inside the square
red
=
vec
.
val
[
2
];
green
=
vec
.
val
[
1
];
blue
=
vec
.
val
[
0
];
ts
->
printf
(
cvtest
::
TS
::
LOG
,
"RED QUADRANT:
\n
"
);
ts
->
printf
(
cvtest
::
TS
::
LOG
,
"Red calculated:
\t\t
%d
\n
"
,
red
);
ts
->
printf
(
cvtest
::
TS
::
LOG
,
"Green calculated:
\t
%d
\n
"
,
green
);
ts
->
printf
(
cvtest
::
TS
::
LOG
,
"Blue calculated:
\t
%d
\n
"
,
blue
);
if
(
red
<
colorThresholdHigh
)
return
false
;
if
(
blue
>
colorThresholdLow
)
return
false
;
if
(
green
>
colorThresholdLow
)
return
false
;
//Checking the second quadrant (with supposed green)
vec
=
m_img
.
at
<
Vec3b
>
(
2
,
7
);
//some point inside the square
red
=
vec
.
val
[
2
];
green
=
vec
.
val
[
1
];
blue
=
vec
.
val
[
0
];
ts
->
printf
(
cvtest
::
TS
::
LOG
,
"GREEN QUADRANT:
\n
"
);
ts
->
printf
(
cvtest
::
TS
::
LOG
,
"Red calculated:
\t\t
%d
\n
"
,
red
);
ts
->
printf
(
cvtest
::
TS
::
LOG
,
"Green calculated:
\t
%d
\n
"
,
green
);
ts
->
printf
(
cvtest
::
TS
::
LOG
,
"Blue calculated:
\t
%d
\n
"
,
blue
);
if
(
green
<
colorThresholdHigh
)
return
false
;
if
(
red
>
colorThresholdLow
)
return
false
;
if
(
blue
>
colorThresholdLow
)
return
false
;
//Checking the third quadrant (with supposed blue)
vec
=
m_img
.
at
<
Vec3b
>
(
7
,
2
);
//some point inside the square
red
=
vec
.
val
[
2
];
green
=
vec
.
val
[
1
];
blue
=
vec
.
val
[
0
];
ts
->
printf
(
cvtest
::
TS
::
LOG
,
"BLUE QUADRANT:
\n
"
);
ts
->
printf
(
cvtest
::
TS
::
LOG
,
"Red calculated:
\t\t
%d
\n
"
,
red
);
ts
->
printf
(
cvtest
::
TS
::
LOG
,
"Green calculated:
\t
%d
\n
"
,
green
);
ts
->
printf
(
cvtest
::
TS
::
LOG
,
"Blue calculated:
\t
%d
\n
"
,
blue
);
if
(
blue
<
colorThresholdHigh
)
return
false
;
if
(
red
>
colorThresholdLow
)
return
false
;
if
(
green
>
colorThresholdLow
)
return
false
;
return
true
;
}
TEST
(
Imgcodecs_jpeg_exif
,
setOrientation
)
{
CV_GrfmtJpegExifOrientationTest
test
;
test
.
safe_run
();
}
#ifdef HAVE_JASPER
TEST
(
Imgcodecs_jasper
,
regression
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment