Skip to content

Commit da475b0

Browse files
authored
perf: jpeg2000 valid_file implementation, much faster than trying to open (#4548)
Primary cost of detecting whether a given file is valid jpeg2000 file is the thread pool initialization and shutdown that `Jpeg2000Input::open` does (the actual thread pool part is inside OpenJpeg code). So add a dedicated `valid_file()` implementation that only needs to check 12 bytes of the header. While at it, I changed already existing `isJp2File()` to `is_jp2_header()` to better match naming conventions used elsewhere, and instead of trying to handle both little and big endian cases by manual repetition of two sets of magic integers, let's do just byte comparisons with `memcmp` instead. On my PC (Ryzen 5950X, SSD, Windows VS2022), doing `ImageInput::create()` on 1138 files where they are not images at all (so OIIO in turns tries all the input plugins on them): - Before: **3.4 seconds** spent in `Jpeg2000Input::open` (1.9s `opj_thread_pool_create`, 1.3s `opj_thread_pool_destroy`) - After: **33 milliseconds** spent in `Jpeg2000Input::valid_file` No new tests. I checked behavior on the official Jpeg2000 conformance data set (https://github.com/uclouvain/openjpeg-data/tree/master/input/conformance), seems to work. Signed-off-by: Aras Pranckevicius <[email protected]>
1 parent 9ee71c4 commit da475b0

File tree

1 file changed

+30
-20
lines changed

1 file changed

+30
-20
lines changed

src/jpeg2000.imageio/jpeg2000input.cpp

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class Jpeg2000Input final : public ImageInput {
8080
return feature == "ioproxy";
8181
// FIXME: we should support Exif/IPTC, but currently don't.
8282
}
83+
bool valid_file(Filesystem::IOProxy* ioproxy) const override;
8384
bool open(const std::string& name, ImageSpec& spec) override;
8485
bool open(const std::string& name, ImageSpec& newspec,
8586
const ImageSpec& config) override;
@@ -97,7 +98,8 @@ class Jpeg2000Input final : public ImageInput {
9798

9899
void init(void);
99100

100-
bool isJp2File(const int* const p_magicTable) const;
101+
static bool is_jp2_header(const uint8_t header[12]);
102+
static bool is_j2k_header(const uint8_t header[5]);
101103

102104
opj_codec_t* create_decompressor();
103105
void destroy_decompressor();
@@ -208,7 +210,19 @@ Jpeg2000Input::init(void)
208210
ioproxy_clear();
209211
}
210212

213+
bool
214+
Jpeg2000Input::valid_file(Filesystem::IOProxy* ioproxy) const
215+
{
216+
if (!ioproxy || ioproxy->mode() != Filesystem::IOProxy::Mode::Read)
217+
return false;
211218

219+
uint8_t header[12];
220+
auto r = ioproxy->pread(header, sizeof(header), 0);
221+
if (r != sizeof(header)) {
222+
return false;
223+
}
224+
return is_jp2_header(header) || is_j2k_header(header);
225+
}
212226

213227
bool
214228
Jpeg2000Input::open(const std::string& name, ImageSpec& p_spec)
@@ -409,36 +423,32 @@ Jpeg2000Input::close(void)
409423
return true;
410424
}
411425

412-
413426
bool
414-
Jpeg2000Input::isJp2File(const int* const p_magicTable) const
427+
Jpeg2000Input::is_jp2_header(const uint8_t header[12])
415428
{
416-
const int32_t JP2_MAGIC = 0x0000000C, JP2_MAGIC2 = 0x0C000000;
417-
if (p_magicTable[0] == JP2_MAGIC || p_magicTable[0] == JP2_MAGIC2) {
418-
const int32_t JP2_SIG1_MAGIC = 0x6A502020, JP2_SIG1_MAGIC2 = 0x2020506A;
419-
const int32_t JP2_SIG2_MAGIC = 0x0D0A870A, JP2_SIG2_MAGIC2 = 0x0A870A0D;
420-
if ((p_magicTable[1] == JP2_SIG1_MAGIC
421-
|| p_magicTable[1] == JP2_SIG1_MAGIC2)
422-
&& (p_magicTable[2] == JP2_SIG2_MAGIC
423-
|| p_magicTable[2] == JP2_SIG2_MAGIC2)) {
424-
return true;
425-
}
426-
}
427-
return false;
429+
const uint8_t jp2_header[] = { 0x0, 0x0, 0x0, 0x0C, 0x6A, 0x50,
430+
0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A };
431+
return memcmp(header, jp2_header, sizeof(jp2_header)) == 0;
428432
}
429433

434+
bool
435+
Jpeg2000Input::is_j2k_header(const uint8_t header[5])
436+
{
437+
const uint8_t j2k_header[] = { 0xFF, 0x4F, 0xFF, 0x51, 0x00 };
438+
return memcmp(header, j2k_header, sizeof(j2k_header)) == 0;
439+
}
430440

431441
opj_codec_t*
432442
Jpeg2000Input::create_decompressor()
433443
{
434-
int magic[3];
435-
auto r = ioproxy()->pread(magic, sizeof(magic), 0);
436-
if (r != 3 * sizeof(int)) {
444+
uint8_t header[12];
445+
auto r = ioproxy()->pread(header, sizeof(header), 0);
446+
if (r != sizeof(header)) {
437447
errorfmt("Empty file \"{}\"", m_filename);
438448
return nullptr;
439449
}
440-
return opj_create_decompress(isJp2File(magic) ? OPJ_CODEC_JP2
441-
: OPJ_CODEC_J2K);
450+
return opj_create_decompress(is_jp2_header(header) ? OPJ_CODEC_JP2
451+
: OPJ_CODEC_J2K);
442452
}
443453

444454

0 commit comments

Comments
 (0)