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
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ link_directories(${LIBJPEG_TURBO_LIB_DIR})

add_library(${PROJECT_NAME} SHARED
image_operations.c
laplace_blending.c
blending.c
jpeg.c
utils.c
)
Expand All @@ -40,7 +40,7 @@ install(DIRECTORY ${LIBJPEG_TURBO_INCLUDE_DIR}/
DESTINATION include)

install(FILES image_operations.h
laplace_blending.h
blending.h
utils.h
jpeg.h
DESTINATION include)
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ If you have `libturbojpeg` installed, compile and run the test with the followin
gcc-14 -O3 -mavx2 -mfma -I simde/ -pthread -fsanitize=address -g -o stitch \
-I../ -I/usr/local/include \
-L/usr/local/lib -lturbojpeg \
stitch.c ../laplace_blending.c ../jpeg.c ../image_operations.c ../utils.c && time ./stitch
stitch.c ../blending.c ../jpeg.c ../image_operations.c ../utils.c && time ./stitch
```

### 2. Testing with Custom-Built NativeSticher Library
Expand Down
126 changes: 114 additions & 12 deletions laplace_blending.c → blending.c
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
#include "blending.h"
#include "jpeg.h"
#include "turbojpeg.h"
#include "utils.h"
#include <assert.h>
#include <math.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "laplace_blending.h"
#include "utils.h"

Blender *create_blender(Rect out_size, int nb) {
Blender *create_multi_band_blender(Rect out_size, int nb) {

Blender *blender = (Blender *)malloc(sizeof(Blender));
blender->real_out_size = out_size;
blender->blender_type = MULTIBAND;
if (!blender)
return NULL;
blender->real_out_size = out_size;

blender->num_bands = min(MAX_BANDS, nb);

Expand Down Expand Up @@ -79,6 +81,41 @@ Blender *create_blender(Rect out_size, int nb) {
return blender;
}

Blender *create_feather_blender(Rect out_size) {
Blender *blender = (Blender *)malloc(sizeof(Blender));
blender->blender_type = FEATHER;
if (!blender)
return NULL;
blender->real_out_size = out_size;
blender->output_size = out_size;
blender->sharpness = 2.5;

blender->out = (ImageF *)malloc(sizeof(ImageF));
blender->final_out = (ImageS *)malloc(sizeof(ImageS));
blender->out_mask = (ImageF *)malloc(sizeof(ImageF));

if (!blender->out || !blender->final_out || !blender->out_mask) {
free(blender->out);
free(blender->final_out);
free(blender->out_mask);
free(blender);
return NULL;
}

blender->out[0] = create_empty_image_f(out_size.width, out_size.height, 3);
blender->out_mask[0] =
create_empty_image_f(out_size.width, out_size.height, 1);

return blender;
}

Blender *create_blender(BlenderType blenderType, Rect out_size, int nb) {
if (blenderType == MULTIBAND) {
return create_multi_band_blender(out_size, nb);
}
return create_feather_blender(out_size);
}

void destroy_blender(Blender *blender) {
if (!blender)
return;
Expand Down Expand Up @@ -148,7 +185,8 @@ void *feed_worker(void *args) {
f->mask_gaussian[f->level].height) {

int outLevelIndex =
((i + f->x_tl) + (k + f->y_tl) * f->out_level_width) * RGB_CHANNELS +
((i + f->x_tl) + (k + f->y_tl) * f->out_level_width) *
RGB_CHANNELS +
z;

float maskVal = f->mask_gaussian[f->level].data[maskIndex];
Expand All @@ -175,7 +213,7 @@ void *feed_worker(void *args) {
return NULL;
}

int feed(Blender *b, Image *img, Image *mask_img, Point tl) {
int multi_band_feed(Blender *b, Image *img, Image *mask_img, Point tl) {
ImageS images[b->num_bands + 1];
int return_val = 1;

Expand Down Expand Up @@ -218,7 +256,8 @@ int feed(Blender *b, Image *img, Image *mask_img, Point tl) {
int bottom = br_new.y - tl.y - img->height;
int right = br_new.x - tl.x - img->width;

add_border_to_image(img, top, bottom, left, right, RGB_CHANNELS, BORDER_REFLECT);
add_border_to_image(img, top, bottom, left, right, RGB_CHANNELS,
BORDER_REFLECT);
add_border_to_image(mask_img, top, bottom, left, right, 1, BORDER_CONSTANT);

images[0] = create_empty_image_s(img->width, img->height, img->channels);
Expand Down Expand Up @@ -300,6 +339,36 @@ int feed(Blender *b, Image *img, Image *mask_img, Point tl) {
return return_val;
}

int feather_feed(Blender *b, Image *img, Image *mask_img, Point tl) {
distance_transform(mask_img);

for (int y = 0; y < img->height; y++) {
for (int x = 0; x < img->width; x++) {
int image_pos = ((y * img->width ) + x) * RGB_CHANNELS;
int mask_pos = (y * img->width) + x;
int result_pos =
((x + tl.x) + ((y + tl.y) * b->output_size.width)) * RGB_CHANNELS;
int result_mask_pos = ((x + tl.x) + ((y + tl.y) * b->output_size.width));
float w = mask_img->data[mask_pos] / 256.0;
b->out_mask->data[result_mask_pos] += w;
for (int z = 0; z < RGB_CHANNELS; z++) {
b->out->data[result_pos + z] += img->data[image_pos + z] * w;
}
}
}

return 1;
}

int feed(Blender *b, Image *img, Image *mask_img, Point tl) {
assert(img->height == mask_img->height && img->width == mask_img->width);
if (b->blender_type == MULTIBAND) {
return multi_band_feed(b, img, mask_img, tl);
} else {
return feather_feed(b, img, mask_img, tl);
}
}

void *blend_worker(void *args) {
ThreadArgs *arg = (ThreadArgs *)args;
int start_row = arg->start_index;
Expand Down Expand Up @@ -340,7 +409,7 @@ void *normalize_worker(void *args) {
return NULL;
}

void blend(Blender *b) {
void multi_band_blend(Blender *b) {
for (int level = 0; level <= b->num_bands; ++level) {
b->final_out[level] = create_empty_image_s(
b->out[level].width, b->out[level].height, b->out[level].channels);
Expand Down Expand Up @@ -393,12 +462,45 @@ void blend(Blender *b) {
}
}

crop_image_buf(&b->result, 0,
max(0, b->result.height - b->real_out_size.height), 0,
max(0, b->result.width - b->real_out_size.width), RGB_CHANNELS);
crop_image_buf(
&b->result, 0, max(0, b->result.height - b->real_out_size.height), 0,
max(0, b->result.width - b->real_out_size.width), RGB_CHANNELS);
free(blended_image.data);
}

void feather_blend(Blender *b) {
b->final_out[0] = create_empty_image_s(b->out[0].width, b->out[0].height,
b->out[0].channels);

NormalThreadData ntd = {b->out[0].width, 0, b->out, b->out_mask,
b->final_out};
WorkerThreadArgs wtd;
wtd.ntd = &ntd;
ParallelOperatorArgs args = {b->out[0].height, &wtd};

parallel_operator(NORMALIZE, &args);
destroy_image_f(&b->out[0]);

b->result.data =
(unsigned char *)malloc(b->output_size.width * b->output_size.height *
RGB_CHANNELS * sizeof(unsigned char));

b->result.channels = RGB_CHANNELS;
b->result.width = b->output_size.width;
b->result.height = b->output_size.height;

convert_images_to_image(&b->final_out[0], &b->result);
destroy_image_s(&b->final_out[0]);
}

void blend(Blender *b) {
if (b->blender_type == MULTIBAND) {
multi_band_blend(b);
} else {
feather_blend(b);
}
}

void parallel_operator(OperatorType operatorType, ParallelOperatorArgs *arg) {
int numThreads = get_cpus_count();
int rowsPerThread = arg->rows / numThreads;
Expand Down
9 changes: 8 additions & 1 deletion laplace_blending.h → blending.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
#include "image_operations.h"

typedef enum{
MULTIBAND,
FEATHER
} BlenderType;

typedef struct
{
int num_bands;
Expand All @@ -13,9 +18,11 @@ typedef struct
Image result;
ImageS *img_laplacians;
ImageS *mask_gaussian;
BlenderType blender_type;
float sharpness;
} Blender;

Blender *create_blender(Rect out_size, int nb);
Blender *create_blender(BlenderType blender_type, Rect out_size, int nb);
int feed(Blender *b, Image *img, Image *maskImg, Point tl);
void blend(Blender *b);
void destroy_blender(Blender *blender);
2 changes: 1 addition & 1 deletion examples/downsampling.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include <stdio.h>
#include <time.h>
#include <string.h>
#include "laplace_blending.h"
#include "blending.h"
#include "utils.h"
#include <stdlib.h>

Expand Down
48 changes: 43 additions & 5 deletions examples/stitch.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
#include <stdio.h>
#include <time.h>
#include <string.h>
#include "laplace_blending.h"
#include "blending.h"
#include "utils.h"

// naive testing
int main()
{
void test_multiband(){
struct timespec start, end;
double duration;

Expand All @@ -24,7 +22,7 @@ int main()
int num_bands = 5;

clock_gettime(CLOCK_MONOTONIC, &start);
Blender *b = create_blender(out_size, num_bands);
Blender *b = create_blender(MULTIBAND,out_size, num_bands);
clock_gettime(CLOCK_MONOTONIC, &end);
duration = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
printf("Elapsed time for creating blended: %.2f seconds\n", duration);
Expand Down Expand Up @@ -63,5 +61,45 @@ int main()
destroy_image(&mask1);
destroy_image(&mask2);

}

void test_feather(){
Image img_buf1 = create_image("../files/apple.jpeg");
Image img_buf2 = create_image("../files/orange.jpeg");


Image mask1 = create_image_mask(img_buf1.width, img_buf1.height, 0.1f, 0, 1);
Image mask2 = create_image_mask(img_buf2.width, img_buf2.height, 0.1f, 1, 0);

int out = (img_buf1.width * 0.1f);
int out_width = (img_buf1.width * 2) - (out * 2);
Rect out_size = {0, 0, out_width, img_buf1.height};

Blender *b = create_blender(FEATHER,out_size, -1);

Point pt1 = {0, 0};
feed(b, &img_buf1, &mask1, pt1);

Point pt2 = {img_buf2.width - out * 2 - 100 , 0};
feed(b, &img_buf2, &mask2, pt2);

blend(b);

if (b->result.data != NULL)
{
if (save_image(&b->result, "merge_feather.jpg"))
{
printf("Merged image saved \n");
}
}


}
// naive testing
int main()
{
test_multiband();
test_feather();

return 0;
}
Loading
Loading