Added support for native image decoding (#808)
This added support for native image decoding on Windows & Apple platforms. This helps us remove libpng & libjpeg completely on these platforms, and in the meantime support more image formats thanks to OS vendors,
This commit is contained in:
Родитель
f90a04606b
Коммит
e424838708
|
@ -71,6 +71,7 @@ option(OCOS_ENABLE_BERT_TOKENIZER "Enable the BertTokenizer building" ON)
|
|||
option(OCOS_ENABLE_BLINGFIRE "Enable operators depending on the Blingfire library" ON)
|
||||
option(OCOS_ENABLE_MATH "Enable math tensor operators building" ON)
|
||||
option(OCOS_ENABLE_DLIB "Enable operators like Inverse depending on DLIB" ON)
|
||||
option(OCOS_ENABLE_VENDOR_IMAGE_CODECS "Enable and use vendor image codecs if supported over libpng & libjpeg" OFF)
|
||||
option(OCOS_ENABLE_OPENCV_CODECS "Enable cv2 and vision operators that require opencv imgcodecs." ON)
|
||||
option(OCOS_ENABLE_CV2 "Enable the operators in `operators/cv2`" ON)
|
||||
option(OCOS_ENABLE_VISION "Enable the operators in `operators/vision`" ON)
|
||||
|
@ -737,11 +738,26 @@ if(OCOS_ENABLE_C_API)
|
|||
list(APPEND _TARGET_LIB_SRC ${audio_TARGET_SRC})
|
||||
endif()
|
||||
if(OCOS_ENABLE_DLIB)
|
||||
include(ext_imgcodecs)
|
||||
file(GLOB cv2_TARGET_SRC "shared/api/c_api_processor.*" "shared/api/image_*.*")
|
||||
list(APPEND _TARGET_LIB_SRC ${cv2_TARGET_SRC})
|
||||
target_include_directories(ocos_operators PUBLIC ${libPNG_SOURCE_DIR} ${libJPEG_SOURCE_DIR})
|
||||
target_link_libraries(ocos_operators PUBLIC ${PNG_LIBRARY} ${JPEG_LIBRARY})
|
||||
if(OCOS_ENABLE_VENDOR_IMAGE_CODECS)
|
||||
add_compile_definitions(OCOS_ENABLE_VENDOR_IMAGE_CODECS)
|
||||
if(WIN32)
|
||||
# Use WIC on Windows. Nothing to be done
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
||||
# Use ImageIO on Apple platforms
|
||||
target_link_libraries(ocos_operators PRIVATE "-framework CoreFoundation" "-framework CoreGraphics" "-framework ImageIO")
|
||||
else()
|
||||
# Fallback to libpng & libjpeg on all other platforms
|
||||
include(ext_imgcodecs)
|
||||
target_include_directories(ocos_operators PUBLIC ${libPNG_SOURCE_DIR} ${libJPEG_SOURCE_DIR})
|
||||
target_link_libraries(ocos_operators PUBLIC ${PNG_LIBRARY} ${JPEG_LIBRARY})
|
||||
endif()
|
||||
else()
|
||||
include(ext_imgcodecs)
|
||||
target_include_directories(ocos_operators PUBLIC ${libPNG_SOURCE_DIR} ${libJPEG_SOURCE_DIR})
|
||||
target_link_libraries(ocos_operators PUBLIC ${PNG_LIBRARY} ${JPEG_LIBRARY})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
set(OCOS_ENABLE_GPT2_TOKENIZER ON CACHE INTERNAL "" FORCE)
|
||||
set(OCOS_ENABLE_C_API ON CACHE INTERNAL "" FORCE)
|
||||
set(OCOS_ENABLE_DLIB ON CACHE INTERNAL "" FORCE)
|
||||
set(OCOS_ENABLE_OPENCV_CODECS OFF CACHE INTERNAL "" FORCE)
|
||||
set(OCOS_ENABLE_CV2 OFF CACHE INTERNAL "" FORCE)
|
||||
set(OCOS_ENABLE_VISION OFF CACHE INTERNAL "" FORCE)
|
||||
set(OCOS_ENABLE_VENDOR_IMAGE_CODECS ON CACHE INTERNAL "" FORCE)
|
||||
set(OCOS_ENABLE_MATH ON CACHE INTERNAL "" FORCE)
|
||||
set(OCOS_ENABLE_AUDIO ON CACHE INTERNAL "" FORCE)
|
||||
|
||||
|
|
|
@ -10,6 +10,20 @@
|
|||
#include "op_def_struct.h"
|
||||
#include "ext_status.h"
|
||||
|
||||
|
||||
OrtxStatus image_decoder(const ortc::Tensor<uint8_t>& input, ortc::Tensor<uint8_t>& output);
|
||||
|
||||
struct DecodeImage {
|
||||
template <typename DictT>
|
||||
OrtxStatus Init(const DictT& attrs) {
|
||||
return {};
|
||||
}
|
||||
|
||||
OrtxStatus Compute(const ortc::Tensor<uint8_t>& input, ortc::Tensor<uint8_t>& output) {
|
||||
return image_decoder(input, output);
|
||||
}
|
||||
};
|
||||
|
||||
class JMemorySourceManager : public jpeg_source_mgr {
|
||||
public:
|
||||
// Constructor
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
#pragma once
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <ImageIO/ImageIO.h>
|
||||
|
||||
#include "op_def_struct.h"
|
||||
#include "ext_status.h"
|
||||
|
||||
struct DecodeImage {
|
||||
template <typename DictT>
|
||||
OrtxStatus Init(const DictT& attrs) {
|
||||
CFStringRef optionKeys[2];
|
||||
CFTypeRef optionValues[2];
|
||||
optionKeys[0] = kCGImageSourceShouldCache;
|
||||
optionValues[0] = (CFTypeRef)kCFBooleanFalse;
|
||||
// Only Integer image data is currently supported
|
||||
optionKeys[1] = kCGImageSourceShouldAllowFloat;
|
||||
optionValues[1] = (CFTypeRef)kCFBooleanFalse;
|
||||
|
||||
imageSourceOptions_ = CFDictionaryCreate(NULL, (const void**)optionKeys, (const void**)optionValues, 2,
|
||||
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
OrtxStatus Compute(const ortc::Tensor<uint8_t>& input, ortc::Tensor<uint8_t>& output) {
|
||||
const auto& dimensions = input.Shape();
|
||||
if (dimensions.size() != 1ULL) {
|
||||
return {kOrtxErrorInvalidArgument, "[ImageDecoder]: Only raw image formats are supported."};
|
||||
}
|
||||
|
||||
// Get data & the length
|
||||
const uint8_t* encoded_image_data = input.Data();
|
||||
const int64_t encoded_image_data_len = input.NumberOfElement();
|
||||
|
||||
// check whether it's too small for a image
|
||||
if (encoded_image_data_len < 8) {
|
||||
return {kOrtxErrorInvalidArgument, "[ImageDecoder]: Invalid image data."};
|
||||
}
|
||||
|
||||
OrtxStatus status{};
|
||||
|
||||
CFDataRef imageData = NULL;
|
||||
CGImageRef image = NULL;
|
||||
CGImageSourceRef imageSource;
|
||||
|
||||
imageData = CFDataCreate(NULL, encoded_image_data, encoded_image_data_len);
|
||||
if (imageData == nullptr) {
|
||||
return {kOrtxErrorInternal, "[ImageDecoder]: Failed to create CFData."};
|
||||
}
|
||||
imageSource = CGImageSourceCreateWithData(imageData, imageSourceOptions_);
|
||||
CFRelease(imageData);
|
||||
|
||||
if (imageSource == nullptr) {
|
||||
return {kOrtxErrorInternal, "[ImageDecoder]: Failed to create CGImageSource."};
|
||||
}
|
||||
|
||||
image = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
|
||||
CFRelease(imageSource);
|
||||
if (image == nullptr) {
|
||||
return {kOrtxErrorInternal, "[ImageDecoder]: Failed to create CGImage."};
|
||||
}
|
||||
|
||||
const int64_t width = static_cast<int64_t>(CGImageGetWidth(image));
|
||||
const int64_t height = static_cast<int64_t>(CGImageGetHeight(image));
|
||||
const int64_t channels = 3;
|
||||
|
||||
std::vector<int64_t> output_dimensions{height, width, channels};
|
||||
uint8_t* decoded_image_data = output.Allocate(output_dimensions);
|
||||
|
||||
if (decoded_image_data == nullptr) {
|
||||
return {kOrtxErrorInvalidArgument, "[ImageDecoder]: Failed to allocate memory for decoded image data."};
|
||||
}
|
||||
|
||||
// CoreGraphics don't support 24BPP. We get 32BPP with alpha first and then extract the 24BPP data we need.
|
||||
const size_t _32bpp_channel = 4;
|
||||
const size_t _32bpp_bytesPerRow = width * _32bpp_channel;
|
||||
const size_t _32bpp_bitmapByteCount = width * height * _32bpp_channel;
|
||||
auto _32bpp_bitmapData = std::make_unique<std::byte[]>(_32bpp_bitmapByteCount);
|
||||
|
||||
// Ask for the sRGB color space.
|
||||
const CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
|
||||
if (colorSpace == nullptr) {
|
||||
return {kOrtxErrorInternal, "[ImageDecoder]: Failed to create CGColorSpace."};
|
||||
}
|
||||
|
||||
const CGBitmapInfo _32bpp_bitmapInfo = kCGBitmapByteOrder32Big | (CGBitmapInfo)kCGImageAlphaPremultipliedLast;
|
||||
CGContextRef context = CGBitmapContextCreate(_32bpp_bitmapData.get(), width, height, 8 /** bitsPerComponent */,
|
||||
_32bpp_bytesPerRow, colorSpace, _32bpp_bitmapInfo);
|
||||
CFRelease(colorSpace);
|
||||
if (context == nullptr) {
|
||||
return {kOrtxErrorInternal, "[ImageDecoder]: Failed to create CGBitmapContext."};
|
||||
}
|
||||
|
||||
const CGRect rect = CGRectMake(0, 0, width, height);
|
||||
CGContextDrawImage(context, rect, image);
|
||||
CFRelease(context);
|
||||
|
||||
uint8_t* ptr = (uint8_t*)_32bpp_bitmapData.get();
|
||||
for (int i = 0; i < width * height; i++) {
|
||||
*(decoded_image_data++) = *(ptr)++; // R
|
||||
*(decoded_image_data++) = *(ptr)++; // G
|
||||
*(decoded_image_data++) = *(ptr)++; // B
|
||||
ptr++; // Skip A
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
~DecodeImage() { CFRelease(imageSourceOptions_); }
|
||||
|
||||
private:
|
||||
CFDictionaryRef imageSourceOptions_{NULL};
|
||||
};
|
|
@ -0,0 +1,149 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#pragma comment(lib, "Windowscodecs.lib")
|
||||
|
||||
#include <wincodec.h>
|
||||
#include <wincodecsdk.h>
|
||||
#include <winrt/base.h>
|
||||
|
||||
#include "op_def_struct.h"
|
||||
#include "ext_status.h"
|
||||
|
||||
|
||||
struct DecodeImage {
|
||||
template <typename DictT>
|
||||
OrtxStatus Init(const DictT& attrs) {
|
||||
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
if (FAILED(hr)) {
|
||||
return {kOrtxErrorInternal, "[ImageDecoder]: Failed when CoInitialize."};
|
||||
}
|
||||
// Create the COM imaging factory
|
||||
hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pIWICFactory_));
|
||||
if (FAILED(hr)) {
|
||||
return {kOrtxErrorInternal, "[ImageDecoder]: Failed to create pIWICFactory."};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
OrtxStatus Compute(const ortc::Tensor<uint8_t>& input, ortc::Tensor<uint8_t>& output) {
|
||||
const auto& dimensions = input.Shape();
|
||||
if (dimensions.size() != 1ULL) {
|
||||
return {kOrtxErrorInvalidArgument, "[ImageDecoder]: Only raw image formats are supported."};
|
||||
}
|
||||
|
||||
// Get data & the length
|
||||
const uint8_t* encoded_image_data = input.Data();
|
||||
const int64_t encoded_image_data_len = input.NumberOfElement();
|
||||
|
||||
// check it's a PNG image or JPEG image
|
||||
if (encoded_image_data_len < 8) {
|
||||
return {kOrtxErrorInvalidArgument, "[ImageDecoder]: Invalid image data."};
|
||||
}
|
||||
|
||||
OrtxStatus status{};
|
||||
|
||||
winrt::com_ptr<IWICBitmapDecoder> pIDecoder;
|
||||
winrt::com_ptr<IWICStream> pIWICStream;
|
||||
winrt::com_ptr<IWICBitmapFrameDecode> pIDecoderFrame;
|
||||
winrt::com_ptr<IWICComponentInfo> pIComponentInfo;
|
||||
WICPixelFormatGUID pixelFormat;
|
||||
|
||||
// Create a WIC stream to map onto the memory.
|
||||
HRESULT hr = pIWICFactory_->CreateStream(pIWICStream.put());
|
||||
if (FAILED(hr)) {
|
||||
return {kOrtxErrorInternal, "[ImageDecoder]: Failed to create pIWICStream."};
|
||||
}
|
||||
|
||||
static_assert(sizeof(uint8_t) == sizeof(unsigned char));
|
||||
|
||||
// Initialize the stream with the memory pointer and size.
|
||||
hr = pIWICStream->InitializeFromMemory((unsigned char*)input.Data(), static_cast<DWORD>(input.NumberOfElement()));
|
||||
if (FAILED(hr)) {
|
||||
return {kOrtxErrorInternal, "[ImageDecoder]: Failed when pIWICStream->InitializeFromMemory."};
|
||||
}
|
||||
|
||||
// Create a decoder for the stream.
|
||||
hr = pIWICFactory_->CreateDecoderFromStream(pIWICStream.get(), // Image to be decoded
|
||||
NULL, // Do not prefer a particular vendor
|
||||
WICDecodeMetadataCacheOnDemand, // Cache metadata when needed
|
||||
pIDecoder.put() // Pointer to the decoder
|
||||
);
|
||||
if (FAILED(hr)) {
|
||||
return {kOrtxErrorInternal, "[ImageDecoder]: Failed to create pIDecoder."};
|
||||
}
|
||||
|
||||
// Retrieve the first bitmap frame.
|
||||
hr = pIDecoder->GetFrame(0, pIDecoderFrame.put());
|
||||
if (FAILED(hr)) {
|
||||
return {kOrtxErrorInternal, "[ImageDecoder]: Failed when pIDecoder->GetFrame."};
|
||||
}
|
||||
|
||||
// Now get a POINTER to an instance of the Pixel Format
|
||||
hr = pIDecoderFrame->GetPixelFormat(&pixelFormat);
|
||||
if (FAILED(hr)) {
|
||||
return {kOrtxErrorInternal, "[ImageDecoder]: Failed when pIDecoderFrame->GetPixelFormat."};
|
||||
}
|
||||
|
||||
hr = pIWICFactory_->CreateComponentInfo(pixelFormat, pIComponentInfo.put());
|
||||
if (FAILED(hr)) {
|
||||
return {kOrtxErrorInternal, "[ImageDecoder]: Failed when pIWICFactory->CreateComponentInfo."};
|
||||
}
|
||||
|
||||
// Get IWICPixelFormatInfo from IWICComponentInfo
|
||||
IWICPixelFormatInfo2* pIPixelFormatInfo = NULL;
|
||||
hr = pIComponentInfo.as(__uuidof(IWICPixelFormatInfo2), reinterpret_cast<void**>(&pIPixelFormatInfo));
|
||||
if (FAILED(hr)) {
|
||||
return {kOrtxErrorInternal, "[ImageDecoder]: Failed to query IWICPixelFormatInfo."};
|
||||
}
|
||||
|
||||
UINT uiWidth = 0;
|
||||
UINT uiHeight = 0;
|
||||
|
||||
hr = pIDecoderFrame->GetSize(&uiWidth, &uiHeight);
|
||||
if (FAILED(hr)) {
|
||||
return {kOrtxErrorInternal, "[ImageDecoder]: pIDecoderFrame->GetSize."};
|
||||
}
|
||||
|
||||
const int height = static_cast<int>(uiHeight);
|
||||
const int width = static_cast<int>(uiWidth);
|
||||
const int channels = 3; // Asks for RGB
|
||||
|
||||
std::vector<int64_t> output_dimensions{height, width, channels};
|
||||
uint8_t* decoded_image_data = output.Allocate(output_dimensions);
|
||||
if (decoded_image_data == nullptr) {
|
||||
return {kOrtxErrorInvalidArgument, "[ImageDecoder]: Failed to allocate memory for decoded image data."};
|
||||
}
|
||||
|
||||
// Convert to 24 bytes per pixel RGB format if needed
|
||||
if (pixelFormat != GUID_WICPixelFormat24bppRGB) {
|
||||
IWICBitmapSource* pConverted = NULL;
|
||||
hr = WICConvertBitmapSource(GUID_WICPixelFormat24bppRGB, pIDecoderFrame.get(), &pConverted);
|
||||
if (FAILED(hr)) {
|
||||
return {kOrtxErrorInternal, "[ImageDecoder]: Failed when WICConvertBitmapSource."};
|
||||
}
|
||||
|
||||
// Upcast to make winrt::com_ptr happy. Should be fine because we only use CopyPixels.
|
||||
pIDecoderFrame.attach((IWICBitmapFrameDecode *)pConverted);
|
||||
}
|
||||
|
||||
const int rowStride = uiWidth * sizeof(uint8_t) * channels;
|
||||
hr = pIDecoderFrame->CopyPixels(NULL, rowStride, static_cast<UINT>(output.SizeInBytes()), decoded_image_data);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return {kOrtxErrorInternal, "[ImageDecoder]: Failed when pIDecoderFrame->CopyPixels."};
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
~DecodeImage() {
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
private:
|
||||
winrt::com_ptr<IWICImagingFactory> pIWICFactory_;
|
||||
};
|
|
@ -8,7 +8,17 @@
|
|||
|
||||
#include "image_processor.h"
|
||||
#include "c_api_utils.hpp"
|
||||
#if OCOS_ENABLE_VENDOR_IMAGE_CODECS
|
||||
#if WIN32
|
||||
#include "image_decoder_win32.hpp"
|
||||
#elif __APPLE__
|
||||
#include "image_decoder_darwin.hpp"
|
||||
#else
|
||||
#include "image_decoder.hpp"
|
||||
#endif
|
||||
#else
|
||||
#include "image_decoder.hpp"
|
||||
#endif
|
||||
#include "image_transforms.hpp"
|
||||
#include "image_transforms_phi_3.hpp"
|
||||
|
||||
|
@ -21,7 +31,7 @@ ort_extensions::LoadRawImages(const std::initializer_list<const char*>& image_pa
|
|||
}
|
||||
|
||||
Operation::KernelRegistry ImageProcessor::kernel_registry_ = {
|
||||
{"DecodeImage", []() { return CreateKernelInstance(image_decoder); }},
|
||||
{"DecodeImage", []() { return CreateKernelInstance(&DecodeImage::Compute); }},
|
||||
{"Resize", []() { return CreateKernelInstance(&Resize::Compute); }},
|
||||
{"Rescale", []() { return CreateKernelInstance(&Rescale::Compute); }},
|
||||
{"Normalize", []() { return CreateKernelInstance(&Normalize::Compute); }},
|
||||
|
|
Двоичный файл не отображается.
|
@ -8,11 +8,24 @@
|
|||
|
||||
#include "gtest/gtest.h"
|
||||
#include "shared/api/c_api_utils.hpp"
|
||||
|
||||
#if OCOS_ENABLE_VENDOR_IMAGE_CODECS
|
||||
#if WIN32
|
||||
#include "shared/api/image_decoder_win32.hpp"
|
||||
#elif __APPLE__
|
||||
#include "shared/api/image_decoder_darwin.hpp"
|
||||
#else
|
||||
#include "shared/api/image_decoder.hpp"
|
||||
#endif
|
||||
#else
|
||||
#include "shared/api/image_decoder.hpp"
|
||||
#endif
|
||||
|
||||
using namespace ort_extensions;
|
||||
|
||||
TEST(ImgDecoderTest, TestPngDecoder) {
|
||||
DecodeImage image_decoder;
|
||||
image_decoder.Init(NULL);
|
||||
std::vector<uint8_t> png_data;
|
||||
std::filesystem::path png_path = "data/processor/exceltable.png";
|
||||
std::ifstream png_file(png_path, std::ios::binary);
|
||||
|
@ -25,8 +38,8 @@ TEST(ImgDecoderTest, TestPngDecoder) {
|
|||
|
||||
ortc::Tensor<uint8_t> png_tensor({static_cast<int64_t>(png_data.size())}, png_data.data());
|
||||
ortc::Tensor<uint8_t> out_tensor{&CppAllocator::Instance()};
|
||||
auto status = image_decoder(png_tensor, out_tensor);
|
||||
ASSERT_TRUE(status.IsOk());
|
||||
auto status = image_decoder.Compute(png_tensor, out_tensor);
|
||||
ASSERT_TRUE(status.IsOk()) << status.ToString();
|
||||
|
||||
ASSERT_EQ(out_tensor.Shape(), std::vector<int64_t>({206, 487, 3}));
|
||||
auto out_range = out_tensor.Data() + 0;
|
||||
|
@ -47,6 +60,8 @@ TEST(ImgDecoderTest, TestPngDecoder) {
|
|||
}
|
||||
|
||||
TEST(ImageDecoderTest, TestJpegDecoder) {
|
||||
DecodeImage image_decoder;
|
||||
image_decoder.Init(NULL);
|
||||
std::vector<uint8_t> jpeg_data;
|
||||
std::filesystem::path jpeg_path = "data/processor/australia.jpg";
|
||||
std::ifstream jpeg_file(jpeg_path, std::ios::binary);
|
||||
|
@ -59,14 +74,41 @@ TEST(ImageDecoderTest, TestJpegDecoder) {
|
|||
|
||||
ortc::Tensor<uint8_t> jpeg_tensor({static_cast<int64_t>(jpeg_data.size())}, jpeg_data.data());
|
||||
ortc::Tensor<uint8_t> out_tensor{&CppAllocator::Instance()};
|
||||
auto status = image_decoder(jpeg_tensor, out_tensor);
|
||||
ASSERT_TRUE(status.IsOk());
|
||||
auto status = image_decoder.Compute(jpeg_tensor, out_tensor);
|
||||
ASSERT_TRUE(status.IsOk()) << status.ToString();
|
||||
|
||||
ASSERT_EQ(out_tensor.Shape(), std::vector<int64_t>({876, 1300, 3}));
|
||||
auto out_range = out_tensor.Data() + 0;
|
||||
ASSERT_EQ(std::vector<uint8_t>(out_range, out_range + 12),
|
||||
std::vector<uint8_t>({48, 14, 5, 48, 14, 5, 48, 14, 5, 48, 14, 5}));
|
||||
|
||||
#if OCOS_ENABLE_VENDOR_IMAGE_CODECS
|
||||
#if WIN32
|
||||
out_range = out_tensor.Data() + 1296 * 3;
|
||||
ASSERT_EQ(std::vector<uint8_t>(out_range, out_range + 12),
|
||||
std::vector<uint8_t>({228, 234, 222, 228, 235, 219, 219, 221, 200, 203, 201, 178}));
|
||||
|
||||
out_range = out_tensor.Data() + 438 * 1300 * 3;
|
||||
ASSERT_EQ(std::vector<uint8_t>(out_range, out_range + 12),
|
||||
std::vector<uint8_t>({84, 68, 53, 86, 70, 55, 92, 76, 60, 101, 86, 65}));
|
||||
|
||||
out_range = out_tensor.Data() + 875 * 1300 * 3 + 1296 * 3;
|
||||
ASSERT_EQ(std::vector<uint8_t>(out_range, out_range + 12),
|
||||
std::vector<uint8_t>({208, 210, 197, 204, 206, 193, 198, 200, 187, 194, 196, 183}));
|
||||
|
||||
#elif __APPLE__
|
||||
out_range = out_tensor.Data() + 1296 * 3;
|
||||
ASSERT_EQ(std::vector<uint8_t>(out_range, out_range + 12),
|
||||
std::vector<uint8_t>({225, 236, 222, 228, 235, 219, 218, 220, 199, 203, 201, 178}));
|
||||
|
||||
out_range = out_tensor.Data() + 438 * 1300 * 3;
|
||||
ASSERT_EQ(std::vector<uint8_t>(out_range, out_range + 12),
|
||||
std::vector<uint8_t>({84, 68, 53, 86, 70, 55, 92, 76, 59, 101, 86, 65}));
|
||||
|
||||
out_range = out_tensor.Data() + 875 * 1300 * 3 + 1296 * 3;
|
||||
ASSERT_EQ(std::vector<uint8_t>(out_range, out_range + 12),
|
||||
std::vector<uint8_t>({209, 211, 198, 204, 206, 193, 198, 200, 187, 194, 196, 183}));
|
||||
#else
|
||||
out_range = out_tensor.Data() + 1296 * 3;
|
||||
ASSERT_EQ(std::vector<uint8_t>(out_range, out_range + 12),
|
||||
std::vector<uint8_t>({221, 237, 224, 225, 236, 219, 218, 222, 199, 203, 202, 174}));
|
||||
|
@ -78,4 +120,58 @@ TEST(ImageDecoderTest, TestJpegDecoder) {
|
|||
out_range = out_tensor.Data() + 875 * 1300 * 3 + 1296 * 3;
|
||||
ASSERT_EQ(std::vector<uint8_t>(out_range, out_range + 12),
|
||||
std::vector<uint8_t>({208, 210, 197, 204, 206, 193, 198, 200, 187, 194, 196, 183}));
|
||||
#endif
|
||||
#else
|
||||
out_range = out_tensor.Data() + 1296 * 3;
|
||||
ASSERT_EQ(std::vector<uint8_t>(out_range, out_range + 12),
|
||||
std::vector<uint8_t>({221, 237, 224, 225, 236, 219, 218, 222, 199, 203, 202, 174}));
|
||||
|
||||
out_range = out_tensor.Data() + 438 * 1300 * 3;
|
||||
ASSERT_EQ(std::vector<uint8_t>(out_range, out_range + 12),
|
||||
std::vector<uint8_t>({84, 68, 55, 86, 70, 55, 92, 77, 58, 101, 86, 65}));
|
||||
|
||||
out_range = out_tensor.Data() + 875 * 1300 * 3 + 1296 * 3;
|
||||
ASSERT_EQ(std::vector<uint8_t>(out_range, out_range + 12),
|
||||
std::vector<uint8_t>({208, 210, 197, 204, 206, 193, 198, 200, 187, 194, 196, 183}));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if OCOS_ENABLE_VENDOR_IMAGE_CODECS
|
||||
#if defined(WIN32) || defined(__APPLE__)
|
||||
TEST(ImageDecoderTest, TestTiffDecoder) {
|
||||
DecodeImage image_decoder;
|
||||
image_decoder.Init(NULL);
|
||||
std::vector<uint8_t> tiff_data;
|
||||
std::filesystem::path tiff_path = "data/processor/canoe.tif";
|
||||
std::ifstream tiff_file(tiff_path, std::ios::binary);
|
||||
ASSERT_TRUE(tiff_file.is_open());
|
||||
tiff_file.seekg(0, std::ios::end);
|
||||
tiff_data.resize(tiff_file.tellg());
|
||||
tiff_file.seekg(0, std::ios::beg);
|
||||
tiff_file.read(reinterpret_cast<char*>(tiff_data.data()), tiff_data.size());
|
||||
tiff_file.close();
|
||||
|
||||
ortc::Tensor<uint8_t> tiff_tensor({static_cast<int64_t>(tiff_data.size())}, tiff_data.data());
|
||||
ortc::Tensor<uint8_t> out_tensor{&CppAllocator::Instance()};
|
||||
auto status = image_decoder.Compute(tiff_tensor, out_tensor);
|
||||
ASSERT_TRUE(status.IsOk()) << status.ToString();
|
||||
|
||||
ASSERT_EQ(out_tensor.Shape(), std::vector<int64_t>({207, 346, 3}));
|
||||
auto out_range = out_tensor.Data() + 0;
|
||||
ASSERT_EQ(std::vector<uint8_t>(out_range, out_range + 12),
|
||||
std::vector<uint8_t>({66, 74, 57, 66, 74, 57, 66, 74, 57, 74, 66, 49}));
|
||||
|
||||
out_range = out_tensor.Data() + 477 * 3;
|
||||
ASSERT_EQ(std::vector<uint8_t>(out_range, out_range + 12),
|
||||
std::vector<uint8_t>({41, 41, 41, 33, 33, 33, 41, 41, 49, 33, 33, 33}));
|
||||
|
||||
out_range = out_tensor.Data() + 103 * 346 * 3;
|
||||
ASSERT_EQ(std::vector<uint8_t>(out_range, out_range + 12),
|
||||
std::vector<uint8_t>({24, 24, 24, 16, 16, 24, 16, 16, 24, 16, 16, 24}));
|
||||
|
||||
out_range = out_tensor.Data() + 206 * 346 * 3 + 342 * 3;
|
||||
ASSERT_EQ(std::vector<uint8_t>(out_range, out_range + 12),
|
||||
std::vector<uint8_t>({82, 66, 49, 74, 66, 57, 74, 66, 49, 82, 74, 57}));
|
||||
}
|
||||
#endif
|
||||
#endif
|
Загрузка…
Ссылка в новой задаче