Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ endif()
#

# general
#option(SD_BUILD_TESTS "sd: build tests" ${SD_STANDALONE})
option(SD_BUILD_TESTS "sd: build tests" OFF)
option(SD_BUILD_EXAMPLES "sd: build examples" ${SD_STANDALONE})
option(SD_WEBP "sd: enable WebP image I/O support" ${SD_WEBP_DEFAULT})
option(SD_USE_SYSTEM_WEBP "sd: link against system libwebp" OFF)
Expand Down Expand Up @@ -352,6 +352,11 @@ if (SD_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()

if (SD_BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()



#
Expand Down
2 changes: 1 addition & 1 deletion examples/common/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2217,7 +2217,7 @@ bool SDGenerationParams::from_json_str(
LOG_ERROR("invalid end_image");
return false;
}
if (!parse_image_array_json_field(j, "ref_images", 3, width, height, ref_images)) {
if (!parse_image_array_json_field(j, "ref_images", 3, 0, 0, ref_images)) {
LOG_ERROR("invalid ref_images");
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion examples/server/routes_openai.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ static bool build_openai_edit_request(const httplib::Request& req,
reinterpret_cast<const char*>(bytes.data()),
static_cast<int>(bytes.size()),
img_w, img_h,
width, height, 3);
0, 0, 3);
if (raw_pixels == nullptr) {
continue;
}
Expand Down
4 changes: 2 additions & 2 deletions examples/server/routes_sdapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,8 @@ static bool build_sdapi_img_gen_request(const json& j,
SDImageOwner image_owner;
if (decode_base64_image(extra_image.get<std::string>(),
3,
request.gen_params.width_and_height_are_set() ? request.gen_params.width : 0,
request.gen_params.width_and_height_are_set() ? request.gen_params.height : 0,
0,
0,
image_owner)) {
const sd_image_t& image = image_owner.get();
request.gen_params.set_width_and_height_if_unset(image.width, image.height);
Expand Down
16 changes: 16 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
add_executable(sd-test-image-decode
image_decode.cpp
../examples/common/common.cpp
../examples/common/log.cpp
../examples/common/media_io.cpp
)

target_include_directories(sd-test-image-decode PRIVATE
"${PROJECT_SOURCE_DIR}/examples"
"${PROJECT_SOURCE_DIR}/src"
)

target_link_libraries(sd-test-image-decode PRIVATE stable-diffusion zip ${CMAKE_THREAD_LIBS_INIT})
target_compile_features(sd-test-image-decode PUBLIC c_std_11 cxx_std_17)

add_test(NAME sd-test-image-decode COMMAND sd-test-image-decode)
105 changes: 105 additions & 0 deletions tests/image_decode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>

#include "common/common.h"
#include "common/media_io.h"

static std::string encode_base64(const std::vector<uint8_t>& bytes) {
static constexpr char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string result;
result.reserve((bytes.size() + 2) / 3 * 4);

for (size_t i = 0; i < bytes.size(); i += 3) {
const uint32_t value = (static_cast<uint32_t>(bytes[i]) << 16) |
(i + 1 < bytes.size() ? static_cast<uint32_t>(bytes[i + 1]) << 8 : 0) |
(i + 2 < bytes.size() ? static_cast<uint32_t>(bytes[i + 2]) : 0);
result.push_back(alphabet[(value >> 18) & 0x3f]);
result.push_back(alphabet[(value >> 12) & 0x3f]);
result.push_back(i + 1 < bytes.size() ? alphabet[(value >> 6) & 0x3f] : '=');
result.push_back(i + 2 < bytes.size() ? alphabet[value & 0x3f] : '=');
}

return result;
}

static bool expect_size(const char* name, const sd_image_t& image, uint32_t width, uint32_t height) {
if (image.width == width && image.height == height) {
return true;
}

std::cerr << name << " has size " << image.width << "x" << image.height << ", expected " << width << "x"
<< height << '\n';
return false;
}

int main() {
const uint8_t pixels[] = {
0,
32,
64,
16,
48,
80,
32,
64,
96,
48,
80,
112,
64,
96,
128,
80,
112,
144,
96,
128,
160,
112,
144,
176,
};
const std::vector<uint8_t> encoded = encode_image_to_vector(EncodedImageFormat::PNG, pixels, 4, 2, 3);
if (encoded.empty()) {
std::cerr << "failed to encode test image\n";
return 1;
}

const std::string image = "data:image/png;base64," + encode_base64(encoded);

int decoded_width = 0;
int decoded_height = 0;
uint8_t* decoded = load_image_from_memory(reinterpret_cast<const char*>(encoded.data()),
static_cast<int>(encoded.size()),
decoded_width,
decoded_height,
0,
0,
3);
if (decoded == nullptr || decoded_width != 4 || decoded_height != 2) {
std::cerr << "decode without expected dimensions did not preserve the source size\n";
free(decoded);
return 1;
}
free(decoded);

SDGenerationParams params;
const std::string json = "{\"width\":8,\"height\":8,\"init_image\":\"" + image +
"\",\"mask_image\":\"" + image + "\",\"ref_images\":[\"" + image +
"\"]}";
if (!params.from_json_str(json)) {
std::cerr << "failed to parse image generation parameters\n";
return 1;
}

if (!expect_size("init_image", params.init_image.get(), 8, 8) ||
!expect_size("mask_image", params.mask_image.get(), 8, 8) ||
params.ref_images.size() != 1 || !expect_size("ref_images[0]", params.ref_images[0].get(), 4, 2)) {
return 1;
}

return 0;
}