* -------------------------------------------------------------------------
* This file is part of the MultimodalSDK project.
* Copyright (c) 2025 Huawei Technologies Co.,Ltd.
*
* MultimodalSDK is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
* Description: Image utils file.
* Author: ACC SDK
* Create: 2025
* History: NA
*/
#include <string>
#include <cstdio>
#include <turbojpeg.h>
#include "acc/ErrorCode.h"
#include "acc/utils/LogImpl.h"
#include "acc/utils/FileUtils.h"
#include "acc/utils/ErrorCodeUtils.h"
#include "acc/utils/ImageUtils.h"
namespace {
constexpr size_t THREE_CHANNEL = 3;
constexpr size_t MAX_WIDTH = 8192;
constexpr size_t MIN_WIDTH = 10;
constexpr size_t MAX_HEIGHT = 8192;
constexpr size_t MIN_HEIGHT = 10;
constexpr size_t IMAGE_SIZE_DIMS = 2;
constexpr size_t IMAGE_MAX_FILE_SIZE = 1024 * 1024 * 50;
}
namespace Acc {
ErrorCode CheckImSize(const std::vector<size_t>& imSize)
{
if (imSize.size() != IMAGE_SIZE_DIMS) {
LogError << "Image size must be a vector of length 2." << GetErrorInfo(ERR_INVALID_PARAM);
return ERR_INVALID_PARAM;
}
if (imSize[0] < MIN_WIDTH || imSize[0] > MAX_WIDTH) {
LogError << "Invalid image width: " << imSize[0] << ". Width must be between " << MIN_WIDTH << " and "
<< MAX_WIDTH << "." << GetErrorInfo(ERR_INVALID_PARAM);
return ERR_INVALID_PARAM;
}
if (imSize[1] < MIN_HEIGHT || imSize[1] > MAX_HEIGHT) {
LogError << "Invalid image height: " << imSize[1] << ". Height must be between " << MIN_HEIGHT << " and "
<< MAX_HEIGHT << "." << GetErrorInfo(ERR_INVALID_PARAM);
return ERR_INVALID_PARAM;
}
return SUCCESS;
}
ErrorCode ReadJpegData(const char* path, std::vector<uint8_t>& rawData, int& width, int& height,
std::shared_ptr<unsigned char[]>& decodedData)
{
if (!IsFileValid(path)) {
LogError << "Image path is invalid.";
return ERR_INVALID_PARAM;
}
if (!CheckFileExtension(path, "jpg") && !CheckFileExtension(path, "jpeg")) {
LogError << "Invalid image suffix, only support 'jpg', 'jpeg', 'JPG' or 'JPEG'.";
return ERR_INVALID_PARAM;
}
ErrorCode ret = ReadFile(path, rawData, IMAGE_MAX_FILE_SIZE);
if (ret != SUCCESS) {
return ret;
}
tjhandle jpegDecompressor = tjInitDecompress();
if (!jpegDecompressor) {
LogError << "Image decompressor initialization failed. "
<< "Image decoding cannot proceed. Please check if the system has proper libjpeg-turbo support installed."
<< GetErrorInfo(ERR_LIBJPEG_INIT_FAILURE);
return ERR_LIBJPEG_INIT_FAILURE;
}
int subSample;
int retInt = tjDecompressHeader2(jpegDecompressor, rawData.data(), rawData.size(), &width, &height, &subSample);
if (retInt != 0) {
LogError << "Invalid image data: failed to parse image header. "
<< "Please ensure the input is a valid, non-corrupted image file."
<< GetErrorInfo(ERR_LIBJPEG_READ_FILE_FAILURE);
tjDestroy(jpegDecompressor);
return ERR_LIBJPEG_READ_FILE_FAILURE;
}
ret = CheckImSize({static_cast<size_t>(width), static_cast<size_t>(height)});
if (ret != SUCCESS) {
LogError << "Check image size failed." << GetErrorInfo(ret);
tjDestroy(jpegDecompressor);
return ret;
}
size_t totalBytes = static_cast<size_t>(width) * static_cast<size_t>(height) * THREE_CHANNEL;
decodedData = std::make_unique<unsigned char[]>(totalBytes);
retInt = tjDecompress2(jpegDecompressor, rawData.data(), rawData.size(), decodedData.get(), width, 0, height,
TJPF_RGB, 0);
if (retInt != 0) {
LogError << "Invalid image data: failed to parse image data. "
<< "Please ensure the input is a valid, non-corrupted image file."
<< GetErrorInfo(ERR_LIBJPEG_READ_FILE_FAILURE);
tjDestroy(jpegDecompressor);
return ERR_LIBJPEG_READ_FILE_FAILURE;
}
tjDestroy(jpegDecompressor);
return SUCCESS;
}
}