Skip to content
Merged
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
5 changes: 3 additions & 2 deletions bindings/c/include/cimg2num.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ void img2num_bilateral_filter(uint8_t *image, size_t width, size_t height, doubl
double sigma_range, uint8_t color_space);

/// @copydoc ::IMG2NUM_H_LABELS_TO_SVG_DOC
char *img2num_labels_to_svg(uint8_t *data, int32_t *labels, const int width, const int height,
const int min_area, const bool draw_contour_borders);
char *img2num_labels_to_svg(const uint8_t *data, const int32_t *labels,
const int width, const int height,
const int min_area);

#ifdef __cplusplus
}
Expand Down
11 changes: 6 additions & 5 deletions bindings/c/src/cimg2num.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,15 @@ void img2num_bilateral_filter(uint8_t *image, size_t width, size_t height, doubl
sigma_spatial, sigma_range, color_space);
}

char *img2num_labels_to_svg(uint8_t *data, int32_t *labels, const int width, const int height,
const int min_area, const bool draw_contour_borders) {
char *img2num_labels_to_svg(const uint8_t *data, const int32_t *labels,
const int width, const int height,
const int min_area) {
char *result{nullptr};
img2num::clear_last_error_and_catch(
[&](uint8_t *d, int32_t *l, int w, int h, int min_a, bool draw_contours) {
result = img2num::labels_to_svg(d, l, w, h, min_a, draw_contours);
[&](const uint8_t *d, const int32_t *l, const int w, const int h, const int min_a) {
result = img2num::labels_to_svg(d, l, w, h, min_a);
},
data, labels, width, height, min_area, draw_contour_borders);
data, labels, width, height, min_area);
return result;
}
}
5 changes: 2 additions & 3 deletions bindings/js/src/wasm_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ EMSCRIPTEN_KEEPALIVE void bilateral_filter(uint8_t *image, size_t width, size_t
}

EMSCRIPTEN_KEEPALIVE char *labels_to_svg(uint8_t *data, int32_t *labels, const int width,
const int height, const int min_area,
const bool draw_contour_borders) {
return img2num_labels_to_svg(data, labels, width, height, min_area, draw_contour_borders);
const int height, const int min_area) {
return img2num_labels_to_svg(data, labels, width, height, min_area);
}
}
5 changes: 3 additions & 2 deletions core/include/img2num.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ void bilateral_filter(uint8_t *image, size_t width, size_t height, double sigma_
double sigma_range, uint8_t color_space);

/// @copydoc IMG2NUM_H_LABELS_TO_SVG_DOC
char *labels_to_svg(uint8_t *data, int32_t *labels, const int width, const int height,
const int min_area, const bool draw_contour_borders);
char *labels_to_svg(const uint8_t *data, const int32_t *labels,
const int width, const int height,
const int min_area);
} // namespace img2num

#endif // IMG2NUM_H
29 changes: 11 additions & 18 deletions core/src/internal/labels_to_svg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,9 @@ format ([r,g,b,a, r,g,b,a, ...]) labels: int32_t* -> output of labelled regions
from K-Means, should be 1/4 the size of data since data is RGBA labels : width *
height : number of pixels in image = 1 : 1 : 1
*/
char *labels_to_svg(uint8_t *data, int32_t *labels, const int width, const int height,
const int min_area, const bool draw_contour_borders) {
char *labels_to_svg(const uint8_t *data, const int32_t *labels,
const int width, const int height,
const int min_area) {
const int32_t num_pixels{width * height};
std::vector<int32_t> labels_vector{labels, labels + num_pixels};
std::vector<int32_t> region_labels;
Expand Down Expand Up @@ -244,24 +245,16 @@ char *labels_to_svg(uint8_t *data, int32_t *labels, const int width, const int h
}
}

// 7. Copy recolored image back
const auto &modified = results.getData();
std::memcpy(data, modified.data(), modified.size() * sizeof(ImageLib::RGBAPixel<uint8_t>));
// 7. Return SVG
std::string svg{contoursResultToSVG(all_contours, width, height)};

// 8. Return SVG if requested
if (!draw_contour_borders) {
std::string svg{contoursResultToSVG(all_contours, width, height)};

// Dynamic C-style allocation (since returned over C ABI)
char *res_svg{static_cast<char *>(std::malloc(svg.size() + 1))};
if (!res_svg) {
return nullptr; // Allocation failed
}
std::memcpy(res_svg, svg.c_str(), svg.size() + 1);

return res_svg;
// Dynamic C-style allocation (since returned over C ABI)
char *res_svg{static_cast<char *>(std::malloc(svg.size() + 1))};
if (!res_svg) {
return nullptr; // Allocation failed
}
std::memcpy(res_svg, svg.c_str(), svg.size() + 1);

return nullptr; // no SVG
return res_svg;
}
} // namespace img2num
6 changes: 5 additions & 1 deletion docs/docs/internal/packages/js/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,11 @@ Messages **optionally** specify bufferKeys as structured descriptors:

```js title="Arguments passed to WASM function"
args: {
(pixels, labels, width, height, min_area, draw_contour_borders);
pixels,
labels,
width,
height,
min_area,
}
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Expand Down
1 change: 0 additions & 1 deletion doxygen/img2num.h.dox
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@
/// @param width Width of the image in pixels.
/// @param height Height of the image in pixels.
/// @param min_area Minimum area (in pixels) for a region to be included in the SVG.
/// @param draw_contour_borders If true, contours of labeled regions will be drawn.
/// @return Pointer to a dynamically allocated C-string containing the SVG data.
/// @note Caller is responsible for freeing the returned string.
/// @note Dox File: `doxygen/img2num.h.dox`
Expand Down
20 changes: 12 additions & 8 deletions example-apps/console-c/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ int main(int argc, char** argv) {
uint8_t* img_data = (uint8_t*)malloc(img_size);
uint8_t* out_data = (uint8_t*)malloc(img_size);
int32_t* out_labels = (int32_t*)malloc(width * height * sizeof(int32_t));
char* res_svg;

if (!img_data || !out_data || !out_labels) {
fprintf(stderr, "Failed to allocate memory\n");
stbi_image_free(image_data_original);
Expand All @@ -69,12 +67,17 @@ int main(int argc, char** argv) {
// Apply bilateral (C API)
double sigma = width * SIGMA_WIDTH_RATIO;
img2num_bilateral_filter(img_data, width, height, sigma, 50.0, 0);

// Apply kmeans (C API)
img2num_kmeans(img_data, out_data, out_labels, width, height, 16, 100, 1);

// Generate SVG
res_svg = img2num_labels_to_svg(img_data, out_labels, width, height, 100, false);
char* res_svg = img2num_labels_to_svg(img_data, out_labels, width, height, 100);

if (res_svg == NULL) {
fprintf(stderr, "Failed to generate SVG\n");
stbi_image_free(image_data_original);
free(img_data); free(out_data); free(out_labels);
return 1;
}

// Save outputs
char out_path[512];
Expand All @@ -87,18 +90,18 @@ int main(int argc, char** argv) {
int exit_code = 0;
const bool blur_save_success = stbi_write_png(out_path, width, height, NUM_CHANNELS, img_data, width * NUM_CHANNELS);
const bool kmeans_save_success = stbi_write_png(kmeans_path, width, height, NUM_CHANNELS, out_data, width * NUM_CHANNELS);

FILE* file = fopen(svg_path, "w");
if (file == NULL) {
printf("Error: Could not open the file!\n");
exit_code == 1;
exit_code = 1;
}

if (exit_code == 0) {
fputs(res_svg, file);
fclose(file);
}

if (blur_save_success && kmeans_save_success && (exit_code == 0)) {
printf("\n\nSUCCESS!\nThe below images have been saved:\n\t- %s\n\t- %s\n\t- %s\n", out_path, kmeans_path, svg_path);
} else {
Expand All @@ -110,5 +113,6 @@ int main(int argc, char** argv) {
free(img_data);
free(out_data);
free(out_labels);
free(res_svg);
return exit_code;
}
42 changes: 23 additions & 19 deletions example-apps/console-cpp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,25 +49,29 @@ int main(int argc, char** argv) {
uint8_t* img_data{new uint8_t[width * height * NUM_CHANNELS]};
uint8_t* out_data{new uint8_t[width * height * NUM_CHANNELS]};
int32_t* out_labels{new int32_t[width * height]};
char* res_svg = nullptr;
for (int ITER=0; ITER<MAX_ITER; ITER++){
std::cout << "Image loaded: " << width << "x" << height << " with " << NUM_CHANNELS << " channel(s)." << std::endl;

// Allocate a copy of the original image
// uint8_t* img_data{new uint8_t[width * height * NUM_CHANNELS]};
std::memcpy(img_data, image_data_original, static_cast<size_t>(width) * static_cast<size_t>(height) * NUM_CHANNELS);

// Apply bilateral
const double sigma{width * SIGMA_WIDTH_RATIO};
img2num::bilateral_filter(img_data, width, height, sigma, 50.0, 0);
// Apply kmeans
img2num::kmeans(img_data, out_data, out_labels, width, height, 32, 100, 1);
// Generate SVG
if (res_svg != nullptr) {
std::free(res_svg);
}
res_svg = img2num::labels_to_svg(img_data, out_labels, width, height, 100, false);

std::cout << "Image loaded: " << width << "x" << height << " with " << NUM_CHANNELS << " channel(s)." << std::endl;

// Allocate a copy of the original image
// uint8_t* img_data{new uint8_t[width * height * NUM_CHANNELS]};
std::memcpy(img_data, image_data_original, static_cast<size_t>(width) * static_cast<size_t>(height) * NUM_CHANNELS);

// Apply bilateral
const double sigma{width * SIGMA_WIDTH_RATIO};
img2num::bilateral_filter(img_data, width, height, sigma, 50.0, 0);
// Apply kmeans
img2num::kmeans(img_data, out_data, out_labels, width, height, 32, 100, 1);
// Generate SVG
char* res_svg{img2num::labels_to_svg(img_data, out_labels, width, height, 100)};
Comment thread
Ryan-Millard marked this conversation as resolved.
if (!res_svg) {
std::cerr << "Failed to generate SVG: allocation failed" << std::endl;
stbi_image_free(image_data_original);
delete[] img_data;
delete[] out_data;
delete[] out_labels;
return 1;
}

// Save the blurred image
std::string out_path{std::string(OUT_DIR) + "/console-cpp-output.png"};
std::string kmeans_path{std::string(OUT_DIR) + "/console-cpp-kmeans.png"};
Expand All @@ -76,7 +80,7 @@ int main(int argc, char** argv) {
int exit_code{0};
const bool blur_save_success{stbi_write_png(out_path.c_str(), width, height, NUM_CHANNELS, img_data, width * NUM_CHANNELS) == 1 ? true : false};
const bool kmeans_save_success{stbi_write_png(kmeans_path.c_str(), width, height, NUM_CHANNELS, out_data, width * NUM_CHANNELS) == 1 ? true : false};

std::ofstream svgFile(svg_path);
if (!svgFile.is_open()) {
std::cerr << "Error: Could not open the file!" << std::endl;
Expand Down
8 changes: 3 additions & 5 deletions packages/js/safeWasmWrappers.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,7 @@ export const kmeans = async ({
* @summary Convert labeled regions to SVG contours.
*
* @description
* Default path: convert an input image and its labeled regions into an SVG.
* `draw_contour_borders`=true: the input image with its contours traced and marked with unique colors.
* Convert an input image and its labeled regions into an SVG.
*
* @async
* @function findContours
Expand All @@ -185,7 +184,6 @@ export const kmeans = async ({
* @property {number} __named_parameters.width - Image width.
* @property {number} __named_parameters.height - Image height.
* @property {number} [__named_parameters.min_area=100] - Minimum area of a region to be considered a contour.
* @property {boolean} [__named_parameters.draw_contour_borders=false] - Whether to draw contour borders in visualization.
* @returns {Promise<{svg: string, visualization: Uint8ClampedArray}>} Generated SVG and optionally pixels with visualized contours.
* @throws {Error} If the WASM function fails or input labels are invalid.
* @example
Expand All @@ -194,10 +192,10 @@ export const kmeans = async ({
* @variation Contour extraction with optional visualization
* @since 0.0.0
*/
export const findContours = async ({ pixels, labels, width, height, min_area = 100, draw_contour_borders = false }) => {
export const findContours = async ({ pixels, labels, width, height, min_area = 100 }) => {
const result = await callWasm({
funcName: "labels_to_svg",
args: { pixels, labels, width, height, min_area, draw_contour_borders },
args: { pixels, labels, width, height, min_area },
bufferKeys: [
{ key: "pixels", type: "Uint8ClampedArray" },
{ key: "labels", type: "Int32Array" },
Expand Down
Loading