Canon cr3 previews (#1958)
* Extract THMB and PRVW images from Canon CR3 file * Added test for Canon CR3 preview extraction. Added test data Canon-R6-pruned.CR3 (first 492016 bytes of https://raw.pixls.us/getfile.php/4659/nice/Canon%20-%20Canon%20EOS%20R6%20-%203:2.CR3). See https://github.com/Exiv2/exiv2/issues/1893 * Fixed format specifier * Update src/bmffimage.cpp Co-authored-by: Miloš Komarčević <4973094+kmilos@users.noreply.github.com> * Update src/bmffimage.cpp Co-authored-by: Miloš Komarčević <4973094+kmilos@users.noreply.github.com> * retrigger checks Co-authored-by: Miloš Komarčević <4973094+kmilos@users.noreply.github.com>
This commit is contained in:
@@ -106,6 +106,28 @@ namespace Exiv2
|
||||
void parseXmp(uint64_t length,uint64_t start);
|
||||
//@}
|
||||
|
||||
//@{
|
||||
/*!
|
||||
@brief Parse a Canon PRVW or THMB box and add an entry to the set
|
||||
of native previews.
|
||||
@param data Buffer containing the box
|
||||
@param out Logging stream
|
||||
@param bTrace Controls logging
|
||||
@param width_offset Index of image width field in data
|
||||
@param height_offset Index of image height field in data
|
||||
@param size_offset Index of image size field in data
|
||||
@param relative_position Location of the start of image data in the file,
|
||||
relative to the current file position indicator.
|
||||
*/
|
||||
void parseCr3Preview(DataBuf &data,
|
||||
std::ostream &out,
|
||||
bool bTrace,
|
||||
uint16_t width_offset,
|
||||
uint16_t height_offset,
|
||||
uint32_t size_offset,
|
||||
uint16_t relative_position);
|
||||
//@}
|
||||
|
||||
//! @name Manipulators
|
||||
//@{
|
||||
void readMetadata() override /* override */;
|
||||
|
||||
+46
-3
@@ -75,7 +75,9 @@
|
||||
#define TAG_cmt4 0x434D5434 /**< "CMT4" gpsID */
|
||||
#define TAG_colr 0x636f6c72 /**< "colr" */
|
||||
#define TAG_exif 0x45786966 /**< "Exif" Used by JXL*/
|
||||
#define TAG_xml 0x786d6c20 /**< "xml" Used by JXL*/
|
||||
#define TAG_xml 0x786d6c20 /**< "xml " Used by JXL*/
|
||||
#define TAG_thmb 0x54484d42 /**< "THMB" Canon thumbnail */
|
||||
#define TAG_prvw 0x50525657 /**< "PRVW" Canon preview image */
|
||||
|
||||
// *****************************************************************************
|
||||
// class member definitions
|
||||
@@ -429,7 +431,12 @@ namespace Exiv2
|
||||
out << " uuidName " << name << std::endl;
|
||||
bLF = false;
|
||||
}
|
||||
if (name == "cano") {
|
||||
if (name == "cano" || name == "canp" ) {
|
||||
if (name == "canp") {
|
||||
// based on
|
||||
// https://github.com/lclevy/canon_cr3/blob/7be75d6/parse_cr3.py#L271
|
||||
io_->seek(8, BasicIo::cur);
|
||||
}
|
||||
while (io_->tell() < box_end) {
|
||||
io_->seek(boxHandler(out,option,box_end,depth + 1), BasicIo::beg);
|
||||
}
|
||||
@@ -456,10 +463,16 @@ namespace Exiv2
|
||||
case TAG_xml:
|
||||
parseXmp(box_length,io_->tell());
|
||||
break;
|
||||
case TAG_thmb:
|
||||
parseCr3Preview(data, out, bTrace, 4, 6, 8, 16);
|
||||
break;
|
||||
case TAG_prvw:
|
||||
parseCr3Preview(data, out, bTrace, 6, 8, 12, 16);
|
||||
break;
|
||||
|
||||
default: break ; /* do nothing */
|
||||
}
|
||||
if ( bLF&& bTrace) out << std::endl;
|
||||
if (bLF && bTrace) out << std::endl;
|
||||
|
||||
// return address of next box
|
||||
return box_end;
|
||||
@@ -540,6 +553,36 @@ namespace Exiv2
|
||||
}
|
||||
}
|
||||
|
||||
void BmffImage::parseCr3Preview(DataBuf &data,
|
||||
std::ostream& out,
|
||||
bool bTrace,
|
||||
uint16_t width_offset,
|
||||
uint16_t height_offset,
|
||||
uint32_t size_offset,
|
||||
uint16_t relative_position)
|
||||
{
|
||||
// Derived from https://github.com/lclevy/canon_cr3
|
||||
NativePreview nativePreview;
|
||||
long here = io_->tell();
|
||||
enforce(here >= 0 &&
|
||||
here <= std::numeric_limits<long>::max() - relative_position,
|
||||
kerCorruptedMetadata);
|
||||
nativePreview.position_ = here + relative_position;
|
||||
nativePreview.width_ = data.read_uint16(width_offset, endian_);
|
||||
nativePreview.height_ = data.read_uint16(height_offset, endian_);
|
||||
nativePreview.size_ = data.read_uint32(size_offset, endian_);
|
||||
nativePreview.filter_ = "";
|
||||
nativePreview.mimeType_ = "image/jpeg";
|
||||
nativePreviews_.push_back(nativePreview);
|
||||
|
||||
if (bTrace) {
|
||||
out << Internal::stringFormat("width,height,size = %u,%u,%u",
|
||||
nativePreview.width_,
|
||||
nativePreview.height_,
|
||||
nativePreview.size_);
|
||||
}
|
||||
}
|
||||
|
||||
void BmffImage::setComment(const std::string& /*comment*/)
|
||||
{
|
||||
// bmff files are read-only
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,65 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import system_tests
|
||||
import unittest
|
||||
from tempfile import TemporaryDirectory
|
||||
import shutil
|
||||
import hashlib
|
||||
import os
|
||||
|
||||
# test needs system_tests.BT.vv['enable_bmff']=1
|
||||
bSkip=system_tests.BT.verbose_version().get('enable_bmff')!='1'
|
||||
|
||||
if bSkip:
|
||||
raise unittest.SkipTest('*** requires enable_bmff=1 ***')
|
||||
|
||||
file_basename = 'Canon-R6-pruned.CR3'
|
||||
previews_expected = (
|
||||
('Canon-R6-pruned-preview1.jpg', 'a182ef12ac883309b4dfc66b87eac1891286d3ae'),
|
||||
('Canon-R6-pruned-preview2.jpg', '524a07f1797854e349ae140e2682ba37147fa6b2')
|
||||
)
|
||||
|
||||
class issue_1893_cr3_preview(metaclass=system_tests.CaseMeta):
|
||||
"""
|
||||
Check that THMB and PRVW images are extracted from Canon CR3 files
|
||||
"""
|
||||
url = "https://github.com/Exiv2/exiv2/issues/1893"
|
||||
filename = "$data_path/" + file_basename
|
||||
commands=[] # see setUp()
|
||||
|
||||
if bSkip:
|
||||
retval=[]
|
||||
stdin=[]
|
||||
stderr=[]
|
||||
stdout=[]
|
||||
print("*** test skipped. requires enable_bmff=1***")
|
||||
else:
|
||||
retval = [ 0, 0]
|
||||
stderr = [ "",""]
|
||||
stdin = [ "", ""]
|
||||
stdout = ["""Preview 1: image/jpeg, 160x120 pixels, 16005 bytes
|
||||
Preview 2: image/jpeg, 1620x1080 pixels, 389450 bytes
|
||||
""", ""]
|
||||
|
||||
def post_tests_hook(self):
|
||||
if self.commands:
|
||||
for j, sha1 in previews_expected:
|
||||
p = os.path.join(self.preview_image_tmp_dir.name, j)
|
||||
self.assertTrue(os.path.isfile(p))
|
||||
h = hashlib.sha1(open(p, 'rb').read()).hexdigest()
|
||||
self.assertEqual(h, sha1)
|
||||
|
||||
def setUp(self):
|
||||
if bSkip:
|
||||
return
|
||||
# Avoid polluting the test data directory with extracted previews
|
||||
self.preview_image_tmp_dir = TemporaryDirectory()
|
||||
shutil.copy(self.expand_variables(self.filename),
|
||||
self.preview_image_tmp_dir.name)
|
||||
p = os.path.join(
|
||||
self.preview_image_tmp_dir.name,
|
||||
file_basename)
|
||||
self.commands = [
|
||||
self.expand_variables("$exiv2 -pp ") + p,
|
||||
self.expand_variables("$exiv2 -ep ") + p
|
||||
]
|
||||
Reference in New Issue
Block a user