struct colorize_priv *cp = (struct colorize_priv *) priv;
guchar *data;
int rs, i, j;
- unsigned char *buffer;
+ unsigned short *buffer;
GtkWidget *dialog;
char *filename = NULL;
data = gdk_pixbuf_get_pixels(cp->pixbuf);
rs = gdk_pixbuf_get_rowstride(cp->pixbuf);
- buffer = malloc(cp->width*cp->height*3);
+ buffer = malloc(cp->width*cp->height*3*sizeof(unsigned short));
for (i = 0; i < cp->height; i++) {
for (j = 0; j < cp->width; j++) {
- buffer[(i*cp->width+j)*3+0] = data[i*rs+j*3+0];
- buffer[(i*cp->width+j)*3+1] = data[i*rs+j*3+1];
- buffer[(i*cp->width+j)*3+2] = data[i*rs+j*3+2];
+ buffer[(i*cp->width+j)*3+0] = data[i*rs+j*3+0] << 8;
+ buffer[(i*cp->width+j)*3+1] = data[i*rs+j*3+1] << 8;
+ buffer[(i*cp->width+j)*3+2] = data[i*rs+j*3+2] << 8;
}
}
int img_width, img_height;
int copy_width, copy_height;
char img_name[256];
-unsigned char *img_grey_buffer = NULL;
+unsigned short *img_grey_buffer = NULL;
unsigned char *img_mark_buffer = NULL, *img_mark_buffer_undo[UNDO_MAX], *img_mark_buffer_copy;
int button_down_x_undo[UNDO_MAX], button_down_y_undo[UNDO_MAX];
int undo_current, undo_num;
extern int button_down_x, button_down_y;
/* load image and create pixbuf */
-void create_image(const char *filename)
+void create_image(const char *filename, int resize)
{
int rc;
static char imgfile[256];
no_mark:
/* create pixbuf */
- create_or_reset_pixbuf(1);
+ create_or_reset_pixbuf(resize);
}
void create_or_reset_pixbuf(int resize)
GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(main_window));
GdkRectangle max;
- /* calculate the maximum windows size, so that the border will not exceed the screen size */
- gdk_window_get_frame_extents(gtk_widget_get_window(main_window), &max);
- max.width -= main_window->allocation.width;
- max.height -= main_window->allocation.height;
- if (max.width < 0)
- max.width = 0;
- if (max.height < 0)
- max.height = 0;
- max.width = gdk_screen_get_width(screen)-max.width;
- max.height = gdk_screen_get_height(screen)-max.height;
-
if (resize && screen) {
+ /* process GTK window event to have correct size */
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ /* calculate the maximum windows size, so that the border will not exceed the screen size */
+ gdk_window_get_frame_extents(gtk_widget_get_window(main_window), &max);
+ max.width -= main_window->allocation.width;
+ max.height -= main_window->allocation.height;
+ if (max.width < 0)
+ max.width = 0;
+ if (max.height < 0)
+ max.height = 0;
+ max.width = gdk_screen_get_width(screen)-max.width;
+ max.height = gdk_screen_get_height(screen)-max.height;
+
try_smaller:
- w = img_scroll->allocation.width-20;
- h = img_scroll->allocation.height-20;
+ w = img_scroll->allocation.width-25;
+ h = img_scroll->allocation.height-25;
if (w < img_width*img_scale_x/16) {
w = main_window->allocation.width - w + img_width*img_scale_x/16;
if (w > max.width) {
for (j = 0; j < w; j++) {
if (preview && !rendered && !flowview) {
/* apply selected color from palette to all pixles */
- _r = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3] / 255.0F;
- _g = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3+1] / 255.0F;
- _b = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3+2] / 255.0F;
+ _r = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3] / 65535.0F;
+ _g = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3+1] / 65535.0F;
+ _b = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3+2] / 65535.0F;
rgb2yuv_pixle(_r, _g, _b, &_y, &_u, &_v);
_y = (_y - 0.5) * mark_palette[mark_selected].contrast + 0.5;
_y += mark_palette[mark_selected].bright;
compose[j*3+2] = _c;
} else {
- compose[j*3] = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3];
- compose[j*3+1] = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3+1];
- compose[j*3+2] = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3+2];
+ compose[j*3] = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3] >> 8;
+ compose[j*3+1] = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3+1] >> 8;
+ compose[j*3+2] = img_grey_buffer[((j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y))*3+2] >> 8;
}
c = img_mark_buffer[(j+x)*16/img_scale_x+img_width*((i+y)*16/img_scale_y)];
if (c > 0 && !flowview && !(rendered && preview)) {
extern GdkPixbuf *img_pixbuf;
extern int img_width, img_height;
extern char img_name[];
-extern unsigned char *img_grey_buffer;
+extern unsigned short *img_grey_buffer;
extern unsigned char *img_mark_buffer;
extern int img_scale_x, img_scale_y;
extern int anything_modified;
-void create_image(const char *filename);
+void create_image(const char *filename, int resize);
void create_or_reset_pixbuf(int resize);
void destroy_image(void);
void draw_image(int x, int y, int w, int h);
static GtkWidget *level_window = NULL;
static GtkObject *black_level_adj;
static GtkObject *white_level_adj;
+static GtkWidget *scale_levels_button;
static GtkObject *fade_level_adj;
+static GtkWidget *yuv_mod_button;
static void level_destroy(GtkWidget *widget, gpointer priv)
{
black_level = GTK_ADJUSTMENT(black_level_adj)->value / 255.0;
white_level = GTK_ADJUSTMENT(white_level_adj)->value / 255.0;
+ scale_levels = GTK_TOGGLE_BUTTON(scale_levels_button)->active;
fade_level = GTK_ADJUSTMENT(fade_level_adj)->value / 100.0;
+ yuv_mod = GTK_TOGGLE_BUTTON(yuv_mod_button)->active;
update_color(mark_selected);
for (i = 0; i < 255; i++)
update_color(i);
GtkWidget *fade_level_label;
GtkWidget *black_level_scroll;
GtkWidget *white_level_scroll;
+ GtkWidget *scale_levels_label;
GtkWidget *fade_level_scroll;
+ GtkWidget *yuv_mod_label;
GtkWidget *button_apply;
GtkWidget *button_reset;
GtkWidget *box;
white_level_scroll = gtk_hscale_new(GTK_ADJUSTMENT(white_level_adj));
gtk_widget_show(white_level_scroll);
+ scale_levels_label = gtk_label_new("Scale levels:");
+ gtk_widget_show(GTK_WIDGET(scale_levels_label));
+ scale_levels_button = gtk_check_button_new();
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(scale_levels_button), scale_levels ? TRUE : FALSE);
+// g_signal_connect(scale_levels_button, "toggled", G_CALLBACK(scale_levels_event), NULL);
+ gtk_widget_show(scale_levels_button);
+
fade_level_label = gtk_label_new("Fade Level (%):");
gtk_widget_show(GTK_WIDGET(fade_level_label));
fade_level_adj = gtk_adjustment_new(fade_level * 100, 0, 101, 1, 1, 1);
fade_level_scroll = gtk_hscale_new(GTK_ADJUSTMENT(fade_level_adj));
gtk_widget_show(fade_level_scroll);
+ yuv_mod_label = gtk_label_new("Use modified YUV:");
+ gtk_widget_show(GTK_WIDGET(yuv_mod_label));
+ yuv_mod_button = gtk_check_button_new();
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(yuv_mod_button), yuv_mod ? TRUE : FALSE);
+// g_signal_connect(yuv_mod_button, "toggled", G_CALLBACK(yuv_mod_event), NULL);
+ gtk_widget_show(yuv_mod_button);
+
/* buttons */
button_reset = gtk_button_new();
g_signal_connect(button_reset, "clicked", G_CALLBACK(reset_event), NULL);
gtk_box_pack_start(GTK_BOX(vbox), black_level_scroll, FALSE, FALSE, 2);
gtk_box_pack_start(GTK_BOX(vbox), white_level_label, FALSE, FALSE, 2);
gtk_box_pack_start(GTK_BOX(vbox), white_level_scroll, FALSE, FALSE, 2);
+ gtk_box_pack_start(GTK_BOX(vbox), scale_levels_label, FALSE, FALSE, 2);
+ gtk_box_pack_start(GTK_BOX(vbox), scale_levels_button, FALSE, FALSE, 2);
gtk_box_pack_start(GTK_BOX(vbox), fade_level_label, FALSE, FALSE, 2);
gtk_box_pack_start(GTK_BOX(vbox), fade_level_scroll, FALSE, FALSE, 2);
+ gtk_box_pack_start(GTK_BOX(vbox), yuv_mod_label, FALSE, FALSE, 2);
+ gtk_box_pack_start(GTK_BOX(vbox), yuv_mod_button, FALSE, FALSE, 2);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 20);
gtk_widget_show(vbox);
GtkColorSelection *colorsel;
static void color_response(GtkDialog *dialog, gint response_id, gpointer user_data)
{
- if (response_id == GTK_RESPONSE_CANCEL) {
- gtk_widget_destroy(colorseldlg);
- colorseldlg = NULL;
- }
- if (response_id == GTK_RESPONSE_OK) {
- GdkColor ncolor;
+ GdkColor ncolor;
+ switch (response_id) {
+ case GTK_RESPONSE_CANCEL:
+ gtk_color_selection_get_previous_color(colorsel, &ncolor);
+new_color:
anything_modified = 1;
- gtk_color_selection_get_current_color(colorsel, &ncolor);
- gtk_color_selection_set_previous_color(colorsel, &ncolor);
mark_palette[mark_selected].r = ncolor.red / 256;
mark_palette[mark_selected].g = ncolor.green / 256;
mark_palette[mark_selected].b = ncolor.blue / 256;
update_color(mark_selected);
draw_image(0, 0, -1, -1);
+ break;
+ case GTK_RESPONSE_OK:
+ gtk_color_selection_get_current_color(colorsel, &ncolor);
+ goto new_color;
}
}
create_timeline(filename);
if (frame_list)
- create_image(frame_list[timeline_selected].filename);
+ create_image(frame_list[timeline_selected].filename, 1);
free(filename);
}
color.green = mark_palette[mark_selected].g * 256 + 128;
color.blue = mark_palette[mark_selected].b * 256 + 128;
- if (colorseldlg)
+ if (colorseldlg) {
+ gtk_color_selection_set_previous_color (colorsel, &color);
gtk_color_selection_set_current_color (colorsel, &color);
+ }
}
/* set current color for bightness+contrast window */
if (argc > 1) {
create_timeline(argv[1]);
if (frame_list)
- create_image(frame_list[timeline_selected].filename);
+ create_image(frame_list[timeline_selected].filename, 1);
}
gtk_main();
timeline_selected = new_frame;
- create_image(frame_list[new_frame].filename);
+ create_image(frame_list[new_frame].filename, 0);
#ifdef WITH_OPENCV
if (flowview && flow_enable && img_grey_buffer)
create_flow_view(
*/
/* scale down image in img_buffer by calculating average */
-static void scale_img(unsigned char *img_buffer, int width, int height, int scale)
+static void scale_img(unsigned short *img_buffer, int width, int height, int scale)
{
int w, h, i, j, x, y;
int r, g, b;
int option_index = 0, c;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
+ {"depth", 1, 0, 'd'},
{"in-itr-num", 1, 0, 'i'},
{"out-itr-num", 1, 0, 'o'},
{"zscale", 1, 0, 'z'},
{0, 0, 0, 0},
};
- c = getopt_long(argc, argv, "hi:o:z:b:f:s:t:", long_options, &option_index);
+ c = getopt_long(argc, argv, "hd:i:o:z:b:f:s:t:", long_options, &option_index);
if (c == -1)
break;
case 'h':
print_help(argv[0]);
exit(0);
+ case 'd':
+ save_depth = atoi(optarg);
+ skip_args += 2;
+ break;
case 'i':
in_itr_num = atoi(optarg);
skip_args += 2;
printf(" Use list to view sequence segments between keyframes.\n");
printf("\nOptions:\n");
printf(" -h --help This help\n");
+ printf(" -d --depth <bits> Save images with given color bit depth (default=%d)\n", save_depth);
printf(" -i --in-itr-num <num> Alter inner iterations (weightening count) of colorization algorithm (default=%d)\n", in_itr_num);
printf(" -o --out-itr-num <num> Alter outer iterations (complete turns) of colorization algorithm (default=%d)\n", out_itr_num);
printf(" -z --zscale <levels> How many grids (staring with the finest) should be scaled in z direction to generate the next coarse grid ");
double diff, sum;
int dims[4];
int w = 0, h = 0, load_w, load_h, k = 1, index = 0, z;
- unsigned char *img_buffer, *img_buffer_all = NULL, *mark_buffer = NULL;
+ unsigned short *img_buffer, *img_buffer_all = NULL;
+ unsigned char *mark_buffer = NULL;
int rc, x, y, i, ii;
time_t start, end;
const char* filename;
#ifdef WITH_OPENCV
if (k > 1 && flow_enable) {
if (!img_buffer_all)
- img_buffer_all = malloc(w*h*3*k);
+ img_buffer_all = malloc(w*h*3*k*sizeof(unsigned short));
if (!img_buffer_all) {
printf("failed to create grey image array\n");
exit (0);
}
- memcpy(img_buffer_all + w*h*3*z, img_buffer, w*h*3);
+ memcpy(img_buffer_all + w*h*3*z, img_buffer, w*h*3*sizeof(unsigned short));
}
#endif
free(img_buffer);
exit (0);
}
printf("Calculating optical flow for %d frames: window=%d\n", k, flow_window/scale);
- }
+ } else if (k > 1)
+ printf("Note: Optical flow is not activated!\n");
for (z = 0; z < k-1; z++) {
if (flow)
create_flow_maps(NULL, img_buffer_all + w*h*3*(z+1), img_buffer_all + w*h*3*z, w, h, flow_window/scale, 0, NULL, NULL, darrayGetPr(flow) + w*h*z, darrayGetPr(flow) + w*h*z + w*h*(k-1), NULL);
if (flow_i)
create_flow_maps(img_buffer_all + w*h*3*z, NULL, img_buffer_all + w*h*3*(z+1), w, h, flow_window/scale, 0, darrayGetPr(flow_i) + w*h*z, darrayGetPr(flow_i) + w*h*z + w*h*(k-1), NULL, NULL, NULL);
}
+#else
+ if (k > 1)
+ printf("Note: Optical flow is not compiled in!\n");
#endif
free(img_buffer_all);
img_buffer_all = NULL;
}
// save result YUV array to image with RGB components
- img_buffer = (unsigned char *)malloc(w * h * 3);
+ img_buffer = (unsigned short *)malloc(w*h*3*sizeof(unsigned short));
if (!img_buffer) {
fprintf(stderr, "Failed to allocate image buffer\n");
return 0;
fprintf(fp, "blacklevel %f\n", black_level);
fprintf(fp, "whitelevel %f\n", white_level);
fprintf(fp, "fadelevel %f\n", fade_level);
+ fprintf(fp, "yuv_mod %d\n", yuv_mod);
fclose(fp);
}
continue;
}
if (!strncmp(line, "fadelevel ", 10)) {
- p = line + 11;
+ p = line + 10;
sscanf(p, "%lf", &fade_level);
continue;
}
+ if (!strncmp(line, "yuv_mod ", 8)) {
+ p = line + 8;
+ sscanf(p, "%d", &yuv_mod);
+ continue;
+ }
/* read palette */
if (i == 256)
* flow_map_y_next = flow map for delta y of next image (result is stored here)
*
*/
-void *create_flow_maps(const unsigned char *img_prev_buffer, const unsigned char *img_next_buffer, unsigned char *img_buffer, int width, int height, int win_size, int steps, double *flow_map_x_prev, double *flow_map_y_prev, double *flow_map_x_next, double *flow_map_y_next, void *_image_preview)
+void *create_flow_maps(const unsigned short *img_prev_buffer, const unsigned short *img_next_buffer, unsigned short *img_buffer, int width, int height, int win_size, int steps, double *flow_map_x_prev, double *flow_map_y_prev, double *flow_map_x_next, double *flow_map_y_next, void *_image_preview)
{
IplImage *image_prev = NULL, *image = NULL, *image_next = NULL, *image_preview = _image_preview;
CvMat *flow;
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
/* copy green channel */
- CV_IMAGE_ELEM(image_prev, uchar, i, j) = img_prev_buffer[(i*width+j)*3 + 1];
+ CV_IMAGE_ELEM(image_prev, uchar, i, j) = img_prev_buffer[(i*width+j)*3 + 1] >> 8;
}
}
}
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
/* copy green channel */
- CV_IMAGE_ELEM(image, uchar, i, j) = img_buffer[(i*width+j)*3 + 1];
+ CV_IMAGE_ELEM(image, uchar, i, j) = img_buffer[(i*width+j)*3 + 1] >> 8;
if (image_preview)
- CV_IMAGE_ELEM(image_preview, uchar, i, j) = img_buffer[(i*width+j)*3 + 1] / 2;
+ CV_IMAGE_ELEM(image_preview, uchar, i, j) = (img_buffer[(i*width+j)*3 + 1] / 2) >> 8;
}
}
}
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
/* copy green channel */
- CV_IMAGE_ELEM(image_next, uchar, i, j) = img_next_buffer[(i*width+j)*3 + 1];
+ CV_IMAGE_ELEM(image_next, uchar, i, j) = img_next_buffer[(i*width+j)*3 + 1] >> 8;
}
}
}
* uv_scale = apply vector (pointing to next plane) by applying uv-color (0 to disable)
*
*/
-void create_flow_view(const char *filename_prev, const char *filename_next, unsigned char *img_buffer, int width, int height, int win_size, int steps, int uv_scale)
+void create_flow_view(const char *filename_prev, const char *filename_next, unsigned short *img_buffer, int width, int height, int win_size, int steps, int uv_scale)
{
- unsigned char *img_prev_buffer = NULL, *img_next_buffer = NULL;
+ unsigned short *img_prev_buffer = NULL, *img_next_buffer = NULL;
double *flow_map_x_prev = NULL, *flow_map_y_prev = NULL, *flow_map_x_next = NULL, *flow_map_y_next = NULL;
IplImage *image_preview = NULL;
CvSize size;
b = (double)c / 255.0;
}
}
- img_buffer[(i*width+j)*3 + 0] = r * 255.0;
- img_buffer[(i*width+j)*3 + 1] = g * 255.0;
- img_buffer[(i*width+j)*3 + 2] = b * 255.0;
+ img_buffer[(i*width+j)*3 + 0] = r * 65535.0;
+ img_buffer[(i*width+j)*3 + 1] = g * 65535.0;
+ img_buffer[(i*width+j)*3 + 2] = b * 65535.0;
}
}
}
void save_flow(const char *filename);
int load_flow(const char *filename);
-void *create_flow_maps(const unsigned char *img_prev_buffer, const unsigned char *img_next_buffer, unsigned char *img_buffer, int width, int height, int win_size, int steps, double *flow_map_x_prev, double *flow_map_y_prev, double *flow_map_x_next, double *flow_map_y_next, void *_image_preview);
+void *create_flow_maps(const unsigned short *img_prev_buffer, const unsigned short *img_next_buffer, unsigned short *img_buffer, int width, int height, int win_size, int steps, double *flow_map_x_prev, double *flow_map_y_prev, double *flow_map_x_next, double *flow_map_y_next, void *_image_preview);
-void create_flow_view(const char *filename_prev, const char *filename_next, unsigned char *img_buffer, int width, int height, int win_size, int steps, int uv_scale);
+void create_flow_view(const char *filename_prev, const char *filename_next, unsigned short *img_buffer, int width, int height, int win_size, int steps, int uv_scale);
#ifdef WITH_MAGICK
#include <magick/api.h>
-/* load given PPM image to memory. return memory pointer, witdth and height
+int save_depth = 16;
+
+/* load given image to memory. return memory pointer, witdth and height
* if offset is given, no memory is allocated */
-int load_img(int offset, unsigned char **buffer, int *width, int *height,
+int load_img(int offset, unsigned short **buffer, int *width, int *height,
const char *filename, int index)
{
int rc = -1;
*height = image->rows;
if (offset < 0) {
- *buffer = (unsigned char *)malloc((*width) * (*height) * 3);
+ *buffer = (unsigned short *)malloc((*width) * (*height) * 3 * sizeof(unsigned short));
if (!*buffer) {
printf("failed to allocate image data\n");
goto exit;
offset = 0;
}
// ExportImagePixels(image, 0, 0, *width, *height, "RGB", CharPixel, *buffer, NULL);
- DispatchImage(image, 0, 0, *width, *height, "RGB", CharPixel, *buffer, NULL);
+ DispatchImage(image, 0, 0, *width, *height, "RGB", ShortPixel, *buffer, NULL);
rc = 0;
}
/* save given image */
-int save_img(unsigned char *buffer, int width, int height, const char *filename,
+int save_img(unsigned short *buffer, int width, int height, const char *filename,
int index)
{
int rc = -1;
imageinfo->quality = 100;
- image=ConstituteImage(width, height, "RGB", CharPixel, buffer, &exception);
+ image=ConstituteImage(width, height, "RGB", ShortPixel, buffer, &exception);
if (!image) {
printf("failed to prepare to write image\n");
goto exit;
}
+
+ /* store as 16 bit, if lib and format supports it */
+ image->depth = save_depth;
+
sprintf(image->filename, filename, index); /* ACHTUNG: nicht imageinfo!!! */
if (!WriteImage(imageinfo, image)) {
printf("failed to write image\n");
/* load given PPM image to memory. return memory pointer, witdth and height
* if offset is given, no memory is allocated */
-int load_img(int offset, unsigned char **buffer, int *width, int *height,
+int load_img(int offset, unsigned short **buffer, int *width, int *height,
const char *filename, int index)
{
FILE *fp;
return -1;
}
if (offset < 0) {
- *buffer = (unsigned char *)malloc((*width) * (*height) * 3 * words);
+ *buffer = (unsigned short *)malloc((*width) * (*height) * 3 * words * sizeof(unsigned short));
if (!*buffer) {
printf("failed to allocate image data\n");
fclose(fp);
return -1;
}
- /* convert 16 to 8 bits */
- if (words == 2) {
- for (i = 0; i < (*width) * (*height) * 3; i++)
- (*buffer)[offset + i] = (*buffer)[offset + i*2];
+ /* convert 8 to 16 bits */
+ if (words == 8) {
+ for (i = (*width) * (*height) * 3 - 1; i >= 0; i--) {
+ (*buffer)[offset + i] = ((unsigned char *)((*buffer) - offset + (i >> 1)))[0] << 8;
+ (*buffer)[offset + i] = ((unsigned char *)((*buffer) - offset + (i >> 1)))[1] << 8;
+ }
+ } else {
+ for (i = 0; i < (*width) * (*height) * 3; i++) {
+ /* correct bit order */
+ (*buffer)[offset + i] = ((unsigned char *)((*buffer) - offset + i))[0] << 8;
+ (*buffer)[offset + i] |= ((unsigned char *)((*buffer) - offset + i))[1];
+ }
}
fclose(fp);
}
/* save given image */
-int save_img(unsigned char *buffer, int width, int height, const char *filename,
+int save_img(unsigned short *buffer, int width, int height, const char *filename,
int index)
{
FILE *fp;
printf("failed to write image\n");
return -1;
}
- fprintf(fp, "P6\n%d %d\n255\n", width, height);
- rc = fwrite(buffer, width * height * 3, 1, fp);
+ fprintf(fp, "P6\n%d %d\n65535\n", width, height);
+ for (i = 0; i < (*width) * (*height) * 3; i++) {
+ /* correct bit order */
+ rc = fputc(((unsigned char *)((*buffer) - offset + i))[0] >> 8, fp);
+ rc = fputc(((unsigned char *)((*buffer) - offset + i))[1], fp);
+ }
+
fclose(fp);
return rc;
/* convert an image to a three dimensional array of double
* the size is: width, height, 3
*/
-void img2array(unsigned char *img, int iw, int ih, double *array, int aw,
+void img2array(unsigned short *img, int iw, int ih, double *array, int aw,
int ah)
{
int x, y;
for (y = 0; y < ih; y++) {
for (x = 0; x < iw; x++) {
- r = img[(x+iw*y)*3] / 255.0F;
- g = img[(x+iw*y)*3+1] / 255.0F;
- b = img[(x+iw*y)*3+2] / 255.0F;
+ r = img[(x+iw*y)*3] / 65535.0F;
+ g = img[(x+iw*y)*3+1] / 65535.0F;
+ b = img[(x+iw*y)*3+2] / 65535.0F;
array[x+aw*y] = r;
array[x+aw*y+plane] = g;
array[x+aw*y+plane+plane] = b;
/* convert a three dimensional array of double to an image
* the size is: width, height, 3
*/
-void array2img(double *array, int aw, int ah, unsigned char *img, int iw,
+void array2img(double *array, int aw, int ah, unsigned short *img, int iw,
int ih)
{
int x, y, c;
r = array[x+aw*y];
g = array[x+aw*y+plane];
b = array[x+aw*y+plane+plane];
- c = (r * 255.0F + 0.5F);
+ c = (r * 65535.0F + 0.5F);
if (c < 0)
c = 0;
- else if (c > 255)
- c = 255;
+ else if (c > 65535)
+ c = 65535;
img[(x+iw*y)*3] = c;
- c = (g * 255.0F + 0.5F);
+ c = (g * 65535.0F + 0.5F);
if (c < 0)
c = 0;
- else if (c > 255)
- c = 255;
+ else if (c > 65535)
+ c = 65535;
img[(x+iw*y)*3+1] = c;
- c = (b * 255.0F + 0.5F);
+ c = (b * 65535.0F + 0.5F);
if (c < 0)
c = 0;
- else if (c > 255)
- c = 255;
+ else if (c > 65535)
+ c = 65535;
img[(x+iw*y)*3+2] = c;
}
}
-int load_img(int offset, unsigned char **buffer, int *width, int *height, const char *filename, int index);
-int save_img(unsigned char *buffer, int width, int height, const char *filename, int index);
-void img2array(unsigned char *img, int iw, int ih, double *array, int aw, int ah);
-void array2img(double *array, int aw, int ah, unsigned char *img, int iw, int ih);
+extern int save_depth;
+int load_img(int offset, unsigned short **buffer, int *width, int *height, const char *filename, int index);
+int save_img(unsigned short *buffer, int width, int height, const char *filename, int index);
+void img2array(unsigned short *img, int iw, int ih, double *array, int aw, int ah);
+void array2img(double *array, int aw, int ah, unsigned short *img, int iw, int ih);
#include "yuv.h"
double black_level = 0.00;
double white_level = 1.00;
+int scale_levels = 0; /* scale between black and white level */
double fade_level = 0.20;
+int yuv_mod = 0; /* UV vectors are not affected by lightness of the pixle */
+
+/* Modified YUV:
+ *
+ * Instead of having U and V vector length affected by the lightnes of the
+ * pixle, we only apply the saturation to U and V. This prevents change of
+ * saturation when convertig YUV /with different Y) back to RGB.
+ */
+
+inline double max_rgb(double r, double g, double b)
+{
+ if (r > g) {
+ if (r > b)
+ return r;
+ else if (b > g)
+ return b;
+ return g;
+ }
+ if (b > g)
+ return b;
+ return g;
+}
+
+/* modified YUV space */
+inline void rgb2yuv_pixle_mod(double r, double g, double b, double *y, double *u, double *v)
+{
+ double max = 0;
+ double r_, g_, b_, y_;
+
+ /* 1. scale RGB to maximum lightness */
+ /* 1.1 find maximum value */
+ max = max_rgb(r, g, b);
+ /* 1.2 if value is too low, assume grey pixle */
+ if (max < 0.001) {
+ *y = *u = *v = 0;
+ return;
+ }
+ /* 1.3 use max to scale RGB to max lightness */
+ r_ = r / max;
+ g_ = g / max;
+ b_ = b / max;
+
+ /* 2. convert to modified YUV */
+ /* 2.1 use Y from original RGB */
+ *y = 0.299*r + 0.587*g + 0.114*b;
+ /* 2.2 use UV from scaled RGB */
+ y_ = 0.299*r_ + 0.587*g_ + 0.114*b_;
+ *u = 0.492*(b_ - y_);
+ *v = 0.877*(r_ - y_);
+}
void rgb2yuv_pixle(double r, double g, double b, double *y, double *u, double *v)
{
+ if (yuv_mod) {
+ rgb2yuv_pixle_mod(r, g, b, y, u, v);
+ return;
+ }
*y = 0.299*r + 0.587*g + 0.114*b;
*u = 0.492*(b - *y);
*v = 0.877*(r - *y);
}
}
-//#define SCALE_LEVEL
-void yuv2rgb_pixle(double y, double u, double v, double *r, double *g, double *b)
+inline int _scale_levels(double *y, double *r, double *g, double *b)
{
- double nu, nv, uv, nuv, ny;
- double fade;
+ double ny;
/* prevent from crashing/artefacts, if black level is at full level */
if (black_level > 0.999) {
*r = *g = *b = 1.0;
- return;
+ return -1;
}
if (white_level < 0.001) {
*r = *g = *b = 0.0;
- return;
+ return -1 ;
}
if (white_level - black_level < 0.001) {
*r = *b = 1.0 ; *g = 0.0;
- return;
+ return -1;
}
/* scale down, black level becomes 0 */
if (black_level > 0.0 || white_level < 1.0) {
- ny = (y - black_level) / (white_level - black_level);
+ ny = (*y - black_level) / (white_level - black_level);
if (ny < 0.0) {
-#ifndef SCALE_LEVEL
- *r = *g = *b = y;
-#else
- *r = *g = *b = 0.0;
-#endif
- return;
+ if (scale_levels)
+ *r = *g = *b = 0.0;
+ else
+ *r = *g = *b = *y;
+ return -1;
}
if (ny > 1.0) {
-#ifndef SCALE_LEVEL
- *r = *g = *b = y;
-#else
- *r = *g = *b = 1.0;
-#endif
- return;
+ if (scale_levels)
+ *r = *g = *b = 1.0;
+ else
+ *r = *g = *b = *y;
+ return -1;
}
- y = ny;
+ *y = ny;
}
+ return 0;
+}
+
+inline void _unscale_levels(double *r, double *g, double *b)
+{
+ if (scale_levels)
+ return;
+ /* scale up, so 0 becomes black level */
+ if (black_level > 0.0 || white_level < 1.0) {
+ *r = black_level + *r * (white_level - black_level);
+ *g = black_level + *g * (white_level - black_level);
+ *b = black_level + *b * (white_level - black_level);
+ }
+}
+
+/* modified YUV space */
+void yuv2rgb_pixle_mod(double y, double u, double v, double *r, double *g, double *b)
+{
+ double r_, g_, b_, y_;
+ double max, scale;
+ double is_dist, must_dist;
+
+ if (_scale_levels(&y, r, g, b))
+ return;
+
+ /* 1. get RGB from U and V with maximum lightness */
+ /* 1.1 U and V conversion without Y */
+ r_ = v/0.877;
+ g_ = -0.395*u - 0.581*v;
+ b_ = u/0.492;
+ /* 1.2 find maximum value */
+ max = max_rgb(r_, g_, b_);
+ /* 1.3 maximize RGB */
+ r_ = 1.0-max+r_;
+ g_ = 1.0-max+g_;
+ b_ = 1.0-max+b_;
+
+ /* 2. calculate scale factor to scale RGB (with max lightness) to target Y */
+ /* 2.1 calculate Y of scaled RGB */
+ y_ = 0.299*r_ + 0.587*g_ + 0.114*b_;
+ /* 2.2 calculate scale factor */
+ scale = y/y_;
+
+ /* 3. scale RGB (with max lightness) to target Y and clip (by reducing saturation), if needed */
+ /* 3.1 scale RGB with factor */
+ r_ *= scale;
+ g_ *= scale;
+ b_ *= scale;
+ /* 3.2 finx max value to clip if needed */
+ max = max_rgb(r_, g_, b_);
+ /* 3.3 clip by reducing saturation */
+ if (max > 1.0) {
+ is_dist = max-y;
+ must_dist = 1-y;
+ *r = (r_-y)/is_dist*must_dist + y;
+ *g = (g_-y)/is_dist*must_dist + y;
+ *b = (b_-y)/is_dist*must_dist + y;
+ } else {
+ *r = r_;
+ *g = g_;
+ *b = b_;
+ }
+
+ _unscale_levels(r, g, b);
+}
+
+//#define SCALE_LEVEL
+void yuv2rgb_pixle(double y, double u, double v, double *r, double *g, double *b)
+{
+ double nu, nv, uv, nuv;
+ double fade;
+
+ if (yuv_mod) {
+ yuv2rgb_pixle_mod(y, u, v, r, g, b);
+ return;
+ }
+
+ if (_scale_levels(&y, r, g, b))
+ return;
+
/* scale UV vector lengh between black level and fade level */
if (y < fade_level && fade_level > 0.001) {
fade = y / fade_level;
*b = y + u/0.492;
}
-#ifndef SCALE_LEVEL
- /* scale up, so 0 becomes black level */
- if (black_level > 0.0 || white_level < 1.0) {
- *r = black_level + *r * (white_level - black_level);
- *g = black_level + *g * (white_level - black_level);
- *b = black_level + *b * (white_level - black_level);
- }
-#endif
+ _unscale_levels(r, g, b);
}
void yuv2rgb(double *src, double *dst, int width, int height)
extern double black_level;
extern double white_level;
+extern int scale_levels;
extern double fade_level;
+extern int yuv_mod;
#include "../src/ppm.h"
/* split interlaced image into two fields */
-static void split_image(const unsigned char *source, unsigned char *dest1,
- unsigned char *dest2, int width, int height)
+static void split_image(const unsigned short *source, unsigned short *dest1,
+ unsigned short *dest2, int width, int height)
{
int y;
for (y = 0; y < (height / 2); y++) {
- memcpy(dest2 + width * y * 3,
- source + width * y * 6, width * 3);
- memcpy(dest1 + width * y * 3,
- source + width * y * 6 + width * 3, width * 3);
+ memcpy(dest2 + width * y * 3, source + width * y * 6,
+ width * 3 * sizeof(unsigned short));
+ memcpy(dest1 + width * y * 3, source + width * y * 6 + width * 3,
+ width * 3 * sizeof(unsigned short));
}
}
/* joint two fields into interlaced image */
-static void join_image(const unsigned char *source1, unsigned char *source2,
- unsigned char *dest, int width, int height)
+static void join_image(const unsigned short *source1, unsigned short *source2,
+ unsigned short *dest, int width, int height)
{
int y;
for (y = 0; y < (height / 2); y++) {
- memcpy(dest + width * y * 6,
- source2 + width * y * 3, width * 3);
- memcpy(dest + width * y * 6 + width * 3,
- source1 + width * y * 3, width * 3);
+ memcpy(dest + width * y * 6, source2 + width * y * 3,
+ width * 3 * sizeof(unsigned short));
+ memcpy(dest + width * y * 6 + width * 3, source1 + width * y * 3,
+ width * 3 * sizeof(unsigned short));
}
}
int main(int argc, char *argv[])
{
- unsigned char *field1_img = NULL, *field2_img = NULL, *frame_img = NULL;
+ unsigned short *field1_img = NULL, *field2_img = NULL, *frame_img = NULL;
int width, height;
int frame, field;
+ int _argc = argc - 1;
+ int _arg = 1;
- if (argc <= 4) {
+ if (_argc > 1 && !strcmp(argv[_arg], "--depth")) {
+ save_depth = atoi(argv[_arg+1]);
+ _arg += 2;
+ _argc -= 2;
+ }
+
+ if (_argc != 4) {
usage:
printf("This tool will split images to fields or join from frames.\n");
printf("The odd lines (second line, fourth line, ...) is the first field.\n\n");
- printf("Usage: %s frame2field <field> <frame> <first>\n", argv[0]);
- printf(" %s field2frame <frame> <field> <first>\n", argv[0]);
+ printf("Usage: %s [--depth 8] frame2field <field> <frame> <first>\n", argv[0]);
+ printf(" %s [--depth 8] field2frame <frame> <field> <first>\n", argv[0]);
+ printf(" --depth <bits> Change color depth from default %d to given bits\n", save_depth);
return 0;
}
- if (!strcmp(argv[1], "frame2field"))
+ if (!strcmp(argv[_arg], "frame2field"))
mode = MODE_FRAME_2_FIELD;
- else if (!strcmp(argv[1], "field2frame"))
+ else if (!strcmp(argv[_arg], "field2frame"))
mode = MODE_FIELD_2_FRAME;
else
goto usage;
- frame = field = atoi(argv[4]);
+ frame = field = atoi(argv[_arg+2]);
next_frame:
printf("\n\nProcessing frame %d, field %d+%d\n", frame, field, field+1);
if (mode == MODE_FRAME_2_FIELD) {
- if (load_img(-1, &frame_img, &width, &height, argv[2], frame++))
+ if (load_img(-1, &frame_img, &width, &height, argv[_arg+1], frame++))
goto out;
- if (!(field1_img = malloc(width * height/2 * 3)))
+ if (!(field1_img = malloc(width * height/2 * 3 * sizeof(unsigned short))))
goto out;
- if (!(field2_img = malloc(width * height/2 * 3)))
+ if (!(field2_img = malloc(width * height/2 * 3 * sizeof(unsigned short))))
goto out;
split_image(frame_img, field1_img, field2_img, width, height);
- if (save_img(field1_img, width, height/2, argv[3], field++))
+ if (save_img(field1_img, width, height/2, argv[_arg+2], field++))
goto out;
- if (save_img(field2_img, width, height/2, argv[3], field++))
+ if (save_img(field2_img, width, height/2, argv[_arg+2], field++))
goto out;
free(frame_img);
free(field1_img);
goto next_frame;
}
if (mode == MODE_FIELD_2_FRAME) {
- if (load_img(-1, &field1_img, &width, &height, argv[2], field++))
+ if (load_img(-1, &field1_img, &width, &height, argv[_arg+1], field++))
goto out;
- if (load_img(-1, &field2_img, &width, &height, argv[2], field++))
+ if (load_img(-1, &field2_img, &width, &height, argv[_arg+1], field++))
goto out;
- if (!(frame_img = malloc(width * height*2 * 3)))
+ if (!(frame_img = malloc(width * height*2 * 3 * sizeof(unsigned short))))
goto out;
join_image(field1_img, field2_img, frame_img, width, height*2);
- if (save_img(frame_img, width, height*2, argv[3], frame++))
+ if (save_img(frame_img, width, height*2, argv[_arg+2], frame++))
goto out;
free(field1_img);
free(field2_img);