Scanner: do not decode whole images, just probe, ref #561

This commit is contained in:
emeric
2025-01-12 20:55:16 +01:00
parent 965c2af662
commit eda06c8214
13 changed files with 238 additions and 51 deletions
+3
View File
@@ -27,6 +27,9 @@ if (${LMS_IMAGE_BACKEND} STREQUAL "stb")
target_sources(lmsimage PRIVATE target_sources(lmsimage PRIVATE
impl/stb/Image.cpp impl/stb/Image.cpp
impl/stb/RawImage.cpp impl/stb/RawImage.cpp
impl/stb/StbImage.cpp
impl/stb/StbImageResize.cpp
impl/stb/StbImageWrite.cpp
) )
target_compile_options(lmsimage PRIVATE "-DSTB_IMAGE_RESIZE_VERSION=${STB_IMAGE_RESIZE_VERSION}") target_compile_options(lmsimage PRIVATE "-DSTB_IMAGE_RESIZE_VERSION=${STB_IMAGE_RESIZE_VERSION}")
target_include_directories(lmsimage PRIVATE ${STB_IMAGE_INCLUDE_DIR}) target_include_directories(lmsimage PRIVATE ${STB_IMAGE_INCLUDE_DIR})
@@ -21,6 +21,8 @@
#include <memory> #include <memory>
#include <Magick++/Image.h>
#include "core/ILogger.hpp" #include "core/ILogger.hpp"
#include "core/ITraceLogger.hpp" #include "core/ITraceLogger.hpp"
#include "image/Exception.hpp" #include "image/Exception.hpp"
@@ -53,6 +55,28 @@ namespace lms::image
return fileExtensions; return fileExtensions;
} }
ImageProperties probeImage(const std::filesystem::path& path)
{
LMS_SCOPED_TRACE_DETAILED("Image", "ProbeFile");
try
{
Magick::Image image;
image.ping(path.c_str());
ImageProperties properties;
properties.width = image.size().width();
properties.height = image.size().height();
return properties;
}
catch (Magick::Exception& e)
{
LMS_LOG(COVER, ERROR, "Caught Magick exception: " << e.what());
throw Exception{ std::string{ "Magick probe error: " } + e.what() };
}
}
std::unique_ptr<IRawImage> decodeImage(std::span<const std::byte> encodedData) std::unique_ptr<IRawImage> decodeImage(std::span<const std::byte> encodedData)
{ {
LMS_SCOPED_TRACE_DETAILED("Image", "DecodeBuffer"); LMS_SCOPED_TRACE_DETAILED("Image", "DecodeBuffer");
+21 -3
View File
@@ -21,14 +21,14 @@
#include <array> #include <array>
#define STB_IMAGE_WRITE_IMPLEMENTATION #include "StbImage.hpp"
#include <stb_image_write.h> #include "StbImageWrite.hpp"
#include "core/ITraceLogger.hpp" #include "core/ITraceLogger.hpp"
#include "image/Exception.hpp"
#include "EncodedImage.hpp" #include "EncodedImage.hpp"
#include "RawImage.hpp" #include "RawImage.hpp"
#include "image/Exception.hpp"
namespace lms::image namespace lms::image
{ {
@@ -42,6 +42,24 @@ namespace lms::image
return fileExtensions; return fileExtensions;
} }
ImageProperties probeImage(const std::filesystem::path& path)
{
LMS_SCOPED_TRACE_DETAILED("Image", "ProbeFile");
int x{};
int y{};
int comp{};
if (::stbi_info(path.c_str(), &x, &y, &comp) == 0)
throw StbiException{ "Probe failed" };
ImageProperties properties;
properties.width = x;
properties.height = y;
return properties;
}
std::unique_ptr<IRawImage> decodeImage(std::span<const std::byte> encodedData) std::unique_ptr<IRawImage> decodeImage(std::span<const std::byte> encodedData)
{ {
LMS_SCOPED_TRACE_DETAILED("Image", "DecodeBuffer"); LMS_SCOPED_TRACE_DETAILED("Image", "DecodeBuffer");
+2 -30
View File
@@ -19,22 +19,8 @@
#include "RawImage.hpp" #include "RawImage.hpp"
#define STB_IMAGE_IMPLEMENTATION #include "StbImage.hpp"
#define STB_IMAGE_RESIZE_IMPLEMENTATION #include "StbImageResize.hpp"
#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL
#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM
#define STBI_FAILURE_USERMSG
#include <stb_image.h>
#if STB_IMAGE_RESIZE_VERSION == 1
#include <stb_image_resize.h>
#elif STB_IMAGE_RESIZE_VERSION == 2
#include <stb_image_resize2.h>
#else
#error "Unhandled STB image resize version"!
#endif
#include "core/ITraceLogger.hpp" #include "core/ITraceLogger.hpp"
#include "image/Exception.hpp" #include "image/Exception.hpp"
@@ -43,21 +29,7 @@ namespace lms::image::STB
{ {
namespace namespace
{ {
class StbiException : public Exception
{
public:
StbiException(std::string_view desc)
: Exception{ std::string{ desc } + ": " + getLastFailureReason() }
{
}
private:
static std::string getLastFailureReason()
{
const char* failureReason{ ::stbi_failure_reason() };
return failureReason ? failureReason : "unknown reason";
}
};
} // namespace } // namespace
RawImage::RawImage(std::span<const std::byte> encodedData) RawImage::RawImage(std::span<const std::byte> encodedData)
+35
View File
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2020 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#define STB_IMAGE_IMPLEMENTATION
#include "StbImage.hpp"
namespace lms::image
{
StbiException::StbiException(std::string_view desc)
: Exception{ std::string{ desc } + ": " + getLastFailureReason() }
{
}
std::string StbiException::getLastFailureReason()
{
const char* failureReason{ ::stbi_failure_reason() };
return failureReason ? failureReason : "unknown reason";
}
} // namespace lms::image
+42
View File
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2020 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define STBI_ONLY_JPEG
#define STBI_ONLY_PNG
#define STBI_ONLY_BMP
#define STBI_FAILURE_USERMSG
#include <stb_image.h>
#include <string_view>
#include "image/Exception.hpp"
namespace lms::image
{
class StbiException : public Exception
{
public:
StbiException(std::string_view desc);
private:
static std::string getLastFailureReason();
};
} // namespace lms::image
@@ -0,0 +1,21 @@
/*
* Copyright (C) 2020 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "StbImageResize.hpp"
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2020 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL
#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM
#if STB_IMAGE_RESIZE_VERSION == 1
#include <stb_image_resize.h>
#elif STB_IMAGE_RESIZE_VERSION == 2
#include <stb_image_resize2.h>
#else
#error "Unhandled STB image resize version"!
#endif
+21
View File
@@ -0,0 +1,21 @@
/*
* Copyright (C) 2020 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "StbImageWrite.hpp"
+21
View File
@@ -0,0 +1,21 @@
/*
* Copyright (C) 2020 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stb_image_write.h>
+3
View File
@@ -31,6 +31,9 @@ namespace lms::image
void init(const std::filesystem::path& path); void init(const std::filesystem::path& path);
std::span<const std::filesystem::path> getSupportedFileExtensions(); std::span<const std::filesystem::path> getSupportedFileExtensions();
// All these methods may throw Exception
ImageProperties probeImage(const std::filesystem::path& path);
std::unique_ptr<IRawImage> decodeImage(std::span<const std::byte> encodedData); std::unique_ptr<IRawImage> decodeImage(std::span<const std::byte> encodedData);
std::unique_ptr<IRawImage> decodeImage(const std::filesystem::path& path); std::unique_ptr<IRawImage> decodeImage(const std::filesystem::path& path);
+7 -1
View File
@@ -22,4 +22,10 @@
namespace lms::image namespace lms::image
{ {
using ImageSize = std::size_t; using ImageSize = std::size_t;
}
struct ImageProperties
{
ImageSize width{};
ImageSize height{};
};
} // namespace lms::image
@@ -27,6 +27,7 @@
#include "database/MediaLibrary.hpp" #include "database/MediaLibrary.hpp"
#include "database/Session.hpp" #include "database/Session.hpp"
#include "image/Exception.hpp" #include "image/Exception.hpp"
#include "image/IRawImage.hpp"
#include "image/Image.hpp" #include "image/Image.hpp"
#include "IFileScanOperation.hpp" #include "IFileScanOperation.hpp"
@@ -55,29 +56,18 @@ namespace lms::scanner
const MediaLibraryInfo _mediaLibrary; const MediaLibraryInfo _mediaLibrary;
db::Db& _db; db::Db& _db;
struct ImageInfo std::optional<image::ImageProperties> _parsedImageProperties;
{
std::size_t height{};
std::size_t width{};
};
std::optional<ImageInfo> _parsedImageInfo;
}; };
void ImageFileScanOperation::scan() void ImageFileScanOperation::scan()
{ {
try try
{ {
std::unique_ptr<image::IRawImage> rawImage{ image::decodeImage(_file) }; _parsedImageProperties = image::probeImage(_file);
ImageInfo imageInfo;
imageInfo.width = rawImage->getWidth();
imageInfo.height = rawImage->getHeight();
_parsedImageInfo = imageInfo;
} }
catch (const image::Exception& e) catch (const image::Exception& e)
{ {
_parsedImageInfo.reset(); _parsedImageProperties.reset();
LMS_LOG(DBUPDATER, ERROR, "Cannot read image in file " << _file << ": " << e.what()); LMS_LOG(DBUPDATER, ERROR, "Cannot read image in file " << _file << ": " << e.what());
} }
} }
@@ -96,7 +86,7 @@ namespace lms::scanner
db::Session& dbSession{ _db.getTLSSession() }; db::Session& dbSession{ _db.getTLSSession() };
db::Image::pointer image{ db::Image::find(dbSession, _file) }; db::Image::pointer image{ db::Image::find(dbSession, _file) };
if (!_parsedImageInfo) if (!_parsedImageProperties)
{ {
if (image) if (image)
{ {
@@ -114,8 +104,8 @@ namespace lms::scanner
image.modify()->setLastWriteTime(fileInfo->lastWriteTime); image.modify()->setLastWriteTime(fileInfo->lastWriteTime);
image.modify()->setFileSize(fileInfo->fileSize); image.modify()->setFileSize(fileInfo->fileSize);
image.modify()->setHeight(_parsedImageInfo->height); image.modify()->setHeight(_parsedImageProperties->height);
image.modify()->setWidth(_parsedImageInfo->width); image.modify()->setWidth(_parsedImageProperties->width);
db::MediaLibrary::pointer mediaLibrary{ db::MediaLibrary::find(dbSession, _mediaLibrary.id) }; // may be null if settings are updated in // => next scan will correct this db::MediaLibrary::pointer mediaLibrary{ db::MediaLibrary::find(dbSession, _mediaLibrary.id) }; // may be null if settings are updated in // => next scan will correct this
image.modify()->setDirectory(utils::getOrCreateDirectory(dbSession, _file.parent_path(), mediaLibrary)); image.modify()->setDirectory(utils::getOrCreateDirectory(dbSession, _file.parent_path(), mediaLibrary));