int32_t dt_control_flip_images_job_run(dt_job_t *job) { long int imgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; const int cw = t1->flag; GList *t = t1->index; int total = g_list_length(t); double fraction=0; char message[512]= {0}; snprintf(message, 512, ngettext ("flipping %d image", "flipping %d images", total), total ); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); while(t) { imgid = (long int)t->data; dt_image_flip(imgid, cw); t = g_list_delete_link(t, t); fraction=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } dt_control_backgroundjobs_destroy(darktable.control, jid); return 0; }
static int32_t dt_camera_import_job_run(dt_job_t *job) { dt_camera_import_t *params = dt_control_job_get_params(job); dt_control_log(_("starting to import images from camera")); if (!dt_import_session_ready(params->shared.session)) { dt_control_log("Failed to import images from camera."); free(params); return 1; } guint total = g_list_length( params->images ); char message[512]= {0}; snprintf(message, sizeof(message), ngettext ("importing %d image from camera", "importing %d images from camera", total), total ); params->bgj = dt_control_backgroundjobs_create(darktable.control, 0, message); // Switch to new filmroll dt_film_open(dt_import_session_film_id(params->shared.session)); dt_ctl_switch_mode_to(DT_LIBRARY); // register listener dt_camctl_listener_t listener= {0}; listener.data=params; listener.image_downloaded=_camera_import_image_downloaded; listener.request_image_path=_camera_request_image_path; listener.request_image_filename=_camera_request_image_filename; // start download of images dt_camctl_register_listener(darktable.camctl,&listener); dt_camctl_import(darktable.camctl, params->camera, params->images); dt_camctl_unregister_listener(darktable.camctl,&listener); dt_control_backgroundjobs_destroy(darktable.control, params->bgj); dt_import_session_destroy(params->shared.session); free(params); return 0; }
int32_t dt_control_duplicate_images_job_run(dt_job_t *job) { long int imgid = -1; long int newimgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; GList *t = t1->index; int total = g_list_length(t); char message[512]= {0}; double fraction=0; snprintf(message, 512, ngettext ("duplicating %d image", "duplicating %d images", total), total ); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); while(t) { imgid = (long int)t->data; newimgid = dt_image_duplicate(imgid); if(newimgid != -1) dt_history_copy_and_paste_on_image(imgid, newimgid, FALSE); t = g_list_delete_link(t, t); fraction=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } dt_control_backgroundjobs_destroy(darktable.control, jid); return 0; }
int32_t dt_image_import_job_run(dt_job_t *job) { int id; char message[512]; dt_image_import_t *t; const guint *jid; t = (dt_image_import_t *)job->param; message[0] = 0; snprintf(message, 512, _("importing image %s"), t->filename); jid = dt_control_backgroundjobs_create(darktable.control, 0, message ); id = dt_image_import(t->film_id, t->filename, TRUE); if(id) { dt_view_filmstrip_set_active_image(darktable.view_manager, id); dt_control_queue_redraw(); } dt_control_backgroundjobs_progress(darktable.control, jid, 1.0); dt_control_backgroundjobs_destroy(darktable.control, jid); return 0; }
int32_t dt_control_export_job_run(dt_job_t *job) { long int imgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; dt_control_export_t *settings = (dt_control_export_t*)t1->data; GList *t = t1->index; const int total = g_list_length(t); int size = 0; dt_imageio_module_format_t *mformat = dt_imageio_get_format_by_index(settings->format_index); g_assert(mformat); dt_imageio_module_storage_t *mstorage = dt_imageio_get_storage_by_index(settings->storage_index); g_assert(mstorage); // Get max dimensions... uint32_t w,h,fw,fh,sw,sh; fw=fh=sw=sh=0; mstorage->dimension(mstorage, &sw,&sh); mformat->dimension(mformat, &fw,&fh); if( sw==0 || fw==0) w=sw>fw?sw:fw; else w=sw<fw?sw:fw; if( sh==0 || fh==0) h=sh>fh?sh:fh; else h=sh<fh?sh:fh; // get shared storage param struct (global sequence counter, one picasa connection etc) dt_imageio_module_data_t *sdata = mstorage->get_params(mstorage, &size); if(sdata == NULL) { dt_control_log(_("failed to get parameters from storage module, aborting export..")); g_free(t1->data); return 1; } dt_control_log(ngettext ("exporting %d image..", "exporting %d images..", total), total); char message[512]= {0}; snprintf(message, 512, ngettext ("exporting %d image to %s", "exporting %d images to %s", total), total, mstorage->name() ); /* create a cancellable bgjob ui template */ const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message ); dt_control_backgroundjobs_set_cancellable(darktable.control, jid, job); const dt_control_t *control = darktable.control; double fraction=0; #ifdef _OPENMP // limit this to num threads = num full buffers - 1 (keep one for darkroom mode) // use min of user request and mipmap cache entries const int full_entries = dt_conf_get_int ("parallel_export"); // GCC won't accept that this variable is used in a macro, considers // it set but not used, which makes for instance Fedora break. const __attribute__((__unused__)) int num_threads = MAX(1, MIN(full_entries, 8)); #if !defined(__SUNOS__) && !defined(__NetBSD__) #pragma omp parallel default(none) private(imgid, size) shared(control, fraction, w, h, stderr, mformat, mstorage, t, sdata, job, jid, darktable, settings) num_threads(num_threads) if(num_threads > 1) #else #pragma omp parallel private(imgid, size) shared(control, fraction, w, h, mformat, mstorage, t, sdata, job, jid, darktable, settings) num_threads(num_threads) if(num_threads > 1) #endif { #endif // get a thread-safe fdata struct (one jpeg struct per thread etc): dt_imageio_module_data_t *fdata = mformat->get_params(mformat, &size); fdata->max_width = settings->max_width; fdata->max_height = settings->max_height; fdata->max_width = (w!=0 && fdata->max_width >w)?w:fdata->max_width; fdata->max_height = (h!=0 && fdata->max_height >h)?h:fdata->max_height; strcpy(fdata->style,settings->style); int num = 0; // Invariant: the tagid for 'darktable|changed' will not change while this function runs. Is this a sensible assumption? guint tagid = 0, etagid = 0; dt_tag_new("darktable|changed",&tagid); dt_tag_new("darktable|exported",&etagid); while(t && dt_control_job_get_state(job) != DT_JOB_STATE_CANCELLED) { #ifdef _OPENMP #pragma omp critical #endif { if(!t) imgid = 0; else { imgid = (long int)t->data; t = g_list_delete_link(t, t); num = total - g_list_length(t); } } // remove 'changed' tag from image dt_tag_detach(tagid, imgid); // make sure the 'exported' tag is set on the image dt_tag_attach(etagid, imgid); // check if image still exists: char imgfilename[DT_MAX_PATH_LEN]; const dt_image_t *image = dt_image_cache_read_get(darktable.image_cache, (int32_t)imgid); if(image) { dt_image_full_path(image->id, imgfilename, DT_MAX_PATH_LEN); if(!g_file_test(imgfilename, G_FILE_TEST_IS_REGULAR)) { dt_control_log(_("image `%s' is currently unavailable"), image->filename); fprintf(stderr, _("image `%s' is currently unavailable"), imgfilename); // dt_image_remove(imgid); dt_image_cache_read_release(darktable.image_cache, image); } else { dt_image_cache_read_release(darktable.image_cache, image); mstorage->store(sdata, imgid, mformat, fdata, num, total, settings->high_quality); } } #ifdef _OPENMP #pragma omp critical #endif { fraction+=1.0/total; dt_control_backgroundjobs_progress(control, jid, fraction); } } #ifdef _OPENMP #pragma omp barrier #pragma omp master #endif { dt_control_backgroundjobs_destroy(control, jid); if(mstorage->finalize_store) mstorage->finalize_store(mstorage, sdata); mstorage->free_params(mstorage, sdata); } // all threads free their fdata mformat->free_params (mformat, fdata); #ifdef _OPENMP } #endif g_free(t1->data); return 0; }
int32_t dt_control_delete_images_job_run(dt_job_t *job) { long int imgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; GList *t = t1->index; int total = g_list_length(t); char message[512]= {0}; double fraction=0; snprintf(message, 512, ngettext ("deleting %d image", "deleting %d images", total), total ); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); sqlite3_stmt *stmt; char query[1024]; sprintf(query, "update images set flags = (flags | %d) where id in (select imgid from selected_images)",DT_IMAGE_REMOVE); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), query, NULL, NULL, NULL); dt_collection_update(darktable.collection); // We need a list of files to regenerate .xmp files if there are duplicates GList *list = NULL; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select distinct folder || '/' || filename from images, film_rolls where images.film_id = film_rolls.id and images.id in (select imgid from selected_images)", -1, &stmt, NULL); if(sqlite3_step(stmt) == SQLITE_ROW) { list = g_list_append(list, g_strdup((const gchar *)sqlite3_column_text(stmt, 0))); } sqlite3_finalize(stmt); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select count(id) from images where filename in (select filename from images where id = ?1) and film_id in (select film_id from images where id = ?1)", -1, &stmt, NULL); while(t) { imgid = (long int)t->data; char filename[DT_MAX_PATH_LEN]; dt_image_full_path(imgid, filename, DT_MAX_PATH_LEN); int duplicates = 0; DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); if(sqlite3_step(stmt) == SQLITE_ROW) duplicates = sqlite3_column_int(stmt, 0); sqlite3_reset(stmt); sqlite3_clear_bindings(stmt); // remove from disk: if(duplicates == 1) // don't remove the actual data if there are (other) duplicates using it (void)g_unlink(filename); dt_image_path_append_version(imgid, filename, DT_MAX_PATH_LEN); char *c = filename + strlen(filename); sprintf(c, ".xmp"); (void)g_unlink(filename); dt_image_remove(imgid); t = g_list_delete_link(t, t); fraction=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } sqlite3_finalize(stmt); char *imgname; while(list) { imgname = (char *)list->data; dt_image_synch_all_xmp(imgname); list = g_list_delete_link(list, list); } g_list_free(list); dt_control_backgroundjobs_destroy(darktable.control, jid); dt_film_remove_empty(); dt_control_queue_redraw_center(); return 0; }
int32_t dt_control_merge_hdr_job_run(dt_job_t *job) { long int imgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; GList *t = t1->index; int total = g_list_length(t); char message[512]= {0}; double fraction=0; snprintf(message, 512, ngettext ("merging %d image", "merging %d images", total), total ); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 1, message); float *pixels = NULL; float *weight = NULL; int wd = 0, ht = 0, first_imgid = -1; uint32_t filter = 0; float whitelevel = 0.0f; total ++; while(t) { imgid = (long int)t->data; dt_mipmap_buffer_t buf; dt_mipmap_cache_read_get(darktable.mipmap_cache, &buf, imgid, DT_MIPMAP_FULL, DT_MIPMAP_BLOCKING); // just take a copy. also do it after blocking read, so filters and bpp will make sense. const dt_image_t *img = dt_image_cache_read_get(darktable.image_cache, imgid); dt_image_t image = *img; dt_image_cache_read_release(darktable.image_cache, img); if(image.filters == 0 || image.bpp != sizeof(uint16_t)) { dt_control_log(_("exposure bracketing only works on raw images")); dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); free(pixels); free(weight); goto error; } filter = dt_image_flipped_filter(img); if(buf.size != DT_MIPMAP_FULL) { dt_control_log(_("failed to get raw buffer from image `%s'"), image.filename); dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); free(pixels); free(weight); goto error; } if(!pixels) { first_imgid = imgid; pixels = (float *)malloc(sizeof(float)*image.width*image.height); weight = (float *)malloc(sizeof(float)*image.width*image.height); memset(pixels, 0x0, sizeof(float)*image.width*image.height); memset(weight, 0x0, sizeof(float)*image.width*image.height); wd = image.width; ht = image.height; } else if(image.width != wd || image.height != ht) { dt_control_log(_("images have to be of same size!")); free(pixels); free(weight); dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); goto error; } // if no valid exif data can be found, assume peleng fisheye at f/16, 8mm, with half of the light lost in the system => f/22 const float eap = image.exif_aperture > 0.0f ? image.exif_aperture : 22.0f; const float efl = image.exif_focal_length > 0.0f ? image.exif_focal_length : 8.0f; const float rad = .5f * efl/eap; const float aperture = M_PI * rad * rad; const float iso = image.exif_iso > 0.0f ? image.exif_iso : 100.0f; const float exp = image.exif_exposure > 0.0f ? image.exif_exposure : 1.0f; const float cal = 100.0f/(aperture*exp*iso); // about proportional to how many photons we can expect from this shot: const float photoncnt = 100.0f*aperture*exp/iso; // stupid, but we don't know the real sensor saturation level: uint16_t saturation = 0; for(int k=0; k<wd*ht; k++) saturation = MAX(saturation, ((uint16_t *)buf.buf)[k]); // seems to be around 64500--64700 for 5dm2 // fprintf(stderr, "saturation: %u\n", saturation); whitelevel = fmaxf(whitelevel, saturation*cal); #ifdef _OPENMP #pragma omp parallel for schedule(static) default(none) shared(buf, pixels, weight, wd, ht, saturation) #endif for(int k=0; k<wd*ht; k++) { const uint16_t in = ((uint16_t *)buf.buf)[k]; // weights based on siggraph 12 poster // zijian zhu, zhengguo li, susanto rahardja, pasi fraenti // 2d denoising factor for high dynamic range imaging float w = envelope(in/(float)saturation) * photoncnt; // in case we are black and drop to zero weight, give it something // just so numerics don't collapse. blown out whites are handled below. if(w < 1e-3f && in < saturation/3) w = 1e-3f; pixels[k] += w * in * cal; weight[k] += w; } t = g_list_delete_link(t, t); /* update backgroundjob ui plate */ fraction+=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); } // normalize by white level to make clipping at 1.0 work as expected (to be sure, scale down one more stop, thus the 0.5): #ifdef _OPENMP #pragma omp parallel for schedule(static) default(none) shared(pixels, wd, ht, weight, whitelevel) #endif for(int k=0; k<wd*ht; k++) { // in case w == 0, all pixels were overexposed (too dark would have been clamped to w >= eps above) if(weight[k] < 1e-3f) pixels[k] = 1.f; // mark as blown out. else // normalize: pixels[k] = fmaxf(0.0f, pixels[k]/(whitelevel*weight[k])); } // output hdr as digital negative with exif data. uint8_t exif[65535]; char pathname[DT_MAX_PATH_LEN]; dt_image_full_path(first_imgid, pathname, DT_MAX_PATH_LEN); // last param is dng mode const int exif_len = dt_exif_read_blob(exif, pathname, first_imgid, 0, wd, ht, 1); char *c = pathname + strlen(pathname); while(*c != '.' && c > pathname) c--; g_strlcpy(c, "-hdr.dng", sizeof(pathname)-(c-pathname)); dt_imageio_write_dng(pathname, pixels, wd, ht, exif, exif_len, filter, 1.0f); dt_control_backgroundjobs_progress(darktable.control, jid, 1.0f); while(*c != '/' && c > pathname) c--; dt_control_log(_("wrote merged hdr `%s'"), c+1); // import new image gchar *directory = g_path_get_dirname((const gchar *)pathname); dt_film_t film; const int filmid = dt_film_new(&film, directory); dt_image_import(filmid, pathname, TRUE); g_free (directory); free(pixels); free(weight); error: dt_control_backgroundjobs_destroy(darktable.control, jid); dt_control_queue_redraw_center(); return 0; }
void dt_film_import1(dt_film_t *film) { gboolean recursive = dt_conf_get_bool("ui_last/import_recursive"); /* first of all gather all images to import */ GList *images = NULL; images = _film_recursive_get_files(film->dirname, recursive, &images); if(g_list_length(images) == 0) { dt_control_log(_("no supported images were found to be imported")); return; } /* we got ourself a list of images, lets sort and start import */ images = g_list_sort(images,(GCompareFunc)_film_filename_cmp); /* let's start import of images */ gchar message[512] = {0}; double fraction = 0; uint32_t total = g_list_length(images); g_snprintf(message, sizeof(message) - 1, ngettext("importing %d image","importing %d images", total), total); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); /* loop thru the images and import to current film roll */ dt_film_t *cfr = film; GList *image = g_list_first(images); do { gchar *cdn = g_path_get_dirname((const gchar *)image->data); /* check if we need to initialize a new filmroll */ if(!cfr || g_strcmp0(cfr->dirname, cdn) != 0) { #if GLIB_CHECK_VERSION (2, 26, 0) if(cfr && cfr->dir) { /* check if we can find a gpx data file to be auto applied to images in the jsut imported filmroll */ g_dir_rewind(cfr->dir); const gchar *dfn = NULL; while ((dfn = g_dir_read_name(cfr->dir)) != NULL) { /* check if we have a gpx to be auto applied to filmroll */ if(strcmp(dfn+strlen(dfn)-4,".gpx") == 0 || strcmp(dfn+strlen(dfn)-4,".GPX") == 0) { gchar *gpx_file = g_build_path (G_DIR_SEPARATOR_S, cfr->dirname, dfn, NULL); dt_control_gpx_apply(gpx_file, cfr->id, dt_conf_get_string("plugins/lighttable/geotagging/tz")); g_free(gpx_file); } } } #endif /* cleanup previously imported filmroll*/ if(cfr && cfr!=film) { if(dt_film_is_empty(cfr->id)) { dt_film_remove(cfr->id); } dt_film_cleanup(cfr); g_free(cfr); cfr = NULL; } /* initialize and create a new film to import to */ cfr = g_malloc(sizeof(dt_film_t)); dt_film_init(cfr); dt_film_new(cfr, cdn); } /* import image */ dt_image_import(cfr->id, (const gchar *)image->data, FALSE); fraction+=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } while( (image = g_list_next(image)) != NULL); // only redraw at the end, to not spam the cpu with exposure events dt_control_queue_redraw_center(); dt_control_signal_raise(darktable.signals,DT_SIGNAL_TAG_CHANGED); dt_control_backgroundjobs_destroy(darktable.control, jid); dt_control_signal_raise(darktable.signals , DT_SIGNAL_FILMROLLS_IMPORTED,film->id); #if GLIB_CHECK_VERSION (2, 26, 0) if(cfr && cfr->dir) { /* check if we can find a gpx data file to be auto applied to images in the just imported filmroll */ g_dir_rewind(cfr->dir); const gchar *dfn = NULL; while ((dfn = g_dir_read_name(cfr->dir)) != NULL) { /* check if we have a gpx to be auto applied to filmroll */ if(strcmp(dfn+strlen(dfn)-4,".gpx") == 0 || strcmp(dfn+strlen(dfn)-4,".GPX") == 0) { gchar *gpx_file = g_build_path (G_DIR_SEPARATOR_S, cfr->dirname, dfn, NULL); dt_control_gpx_apply(gpx_file, cfr->id, dt_conf_get_string("plugins/lighttable/geotagging/tz")); g_free(gpx_file); } } } #endif }
int32_t dt_control_indexer_job_run(dt_job_t *job) { // if no indexing was requested, bail out: if(!dt_conf_get_bool("run_similarity_indexer")) return 0; /* * First pass run thru ALL images and collect the ones who needs to update * \TODO in the future lets have a indexer table with ids filed with images * thats need some kind of reindexing.. all mark dirty functions adds image * to this table-- */ // temp memory for uncompressed images: uint8_t *scratchmem = dt_mipmap_cache_alloc_scratchmem(darktable.mipmap_cache); GList *images=NULL; sqlite3_stmt *stmt; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select images.id,film_rolls.folder||'/'||images.filename,images.histogram,images.lightmap from images,film_rolls where film_rolls.id = images.film_id", -1, &stmt, NULL); while(sqlite3_step(stmt) == SQLITE_ROW) { _control_indexer_img_t *idximg=g_malloc(sizeof( _control_indexer_img_t)); memset(idximg,0,sizeof(_control_indexer_img_t)); idximg->id = sqlite3_column_int(stmt,0); /* first check if image file exists on disk */ const char *filename = (const char *)sqlite3_column_text(stmt, 1); if (filename && !g_file_test(filename, G_FILE_TEST_IS_REGULAR)) idximg->flags |= _INDEXER_IMAGE_FILE_REMOVED; /* check if histogram should be updated */ if (sqlite3_column_bytes(stmt, 2) != sizeof(dt_similarity_histogram_t)) idximg->flags |= _INDEXER_UPDATE_HISTOGRAM; /* check if lightmap should be updated */ if (sqlite3_column_bytes(stmt, 3) != sizeof(dt_similarity_lightmap_t)) idximg->flags |= _INDEXER_UPDATE_LIGHTMAP; /* if image is flagged add to collection */ if (idximg->flags != 0) images = g_list_append(images, idximg); else g_free(idximg); } sqlite3_finalize(stmt); /* * Second pass, run thru collected images thats * need reindexing... */ GList *imgitem = g_list_first(images); if(imgitem) { char message[512]= {0}; double fraction=0; int total = g_list_length(images); guint *jid = NULL; /* background job plate only if more then one image is reindexed */ if (total > 1) { snprintf(message, 512, ngettext ("re-indexing %d image", "re-indexing %d images", total), total ); jid = (guint *)dt_control_backgroundjobs_create(darktable.control, 0, message); } do { // bail out if we're shutting down: if(!dt_control_running()) break; // if indexer was switched off during runtime, respect that as soon as we can: if(!dt_conf_get_bool("run_similarity_indexer")) break; /* get the _control_indexer_img_t pointer */ _control_indexer_img_t *idximg = imgitem->data; /* * Check if image has been delete from disk */ if ((idximg->flags&_INDEXER_IMAGE_FILE_REMOVED)) { /* file does not exist on disk lets delete image reference from database */ //char query[512]={0}; // \TODO dont delete move to an temp table and let user to revalidate /*sprintf(query,"delete from history where imgid=%d",idximg->id); DT_DEBUG_SQLITE3_EXEC(darktable.db, query, NULL, NULL, NULL); sprintf(query,"delete from tagged_images where imgid=%d",idximg->id); DT_DEBUG_SQLITE3_EXEC(darktable.db, query, NULL, NULL, NULL); sprintf(query,"delete from images where id=%d",idximg->id); DT_DEBUG_SQLITE3_EXEC(darktable.db, query, NULL, NULL, NULL);*/ /* no need to additional work */ continue; } /* * Check if image histogram or lightmap should be updated. * those indexing that involves a image pipe should fall into this */ if ( (idximg->flags&_INDEXER_UPDATE_HISTOGRAM) || (idximg->flags&_INDEXER_UPDATE_LIGHTMAP) ) { /* get a mipmap of image to analyse */ dt_mipmap_buffer_t buf; dt_mipmap_cache_read_get(darktable.mipmap_cache, &buf, idximg->id, DT_MIPMAP_2, DT_MIPMAP_BLOCKING); // pointer owned by the cache or == scratchmem, no need to free this one: uint8_t *buf_decompressed = dt_mipmap_cache_decompress(&buf, scratchmem); if (!(buf.width * buf.height)) continue; /* * Generate similarity histogram data if requested */ if ( (idximg->flags&_INDEXER_UPDATE_HISTOGRAM) ) { dt_similarity_histogram_t histogram; float bucketscale = (float)DT_SIMILARITY_HISTOGRAM_BUCKETS/(float)0xff; for(int j=0; j<(4*buf.width*buf.height); j+=4) { /* swap rgb and scale to bucket index */ uint8_t rgb[3]; for(int k=0; k<3; k++) rgb[k] = (int)((buf_decompressed[j+2-k]/(float)0xff) * bucketscale); /* distribute rgb into buckets */ for(int k=0; k<3; k++) histogram.rgbl[rgb[k]][k]++; /* distribute lum into buckets */ uint8_t lum = MAX(MAX(rgb[0], rgb[1]), rgb[2]); histogram.rgbl[lum][3]++; } for(int k=0; k<DT_SIMILARITY_HISTOGRAM_BUCKETS; k++) for (int j=0; j<4; j++) histogram.rgbl[k][j] /= (buf.width*buf.height); /* store the histogram data */ dt_similarity_histogram_store(idximg->id, &histogram); } /* * Generate scaledowned similarity lightness map if requested */ if ( (idximg->flags&_INDEXER_UPDATE_LIGHTMAP) ) { dt_similarity_lightmap_t lightmap; memset(&lightmap,0,sizeof(dt_similarity_lightmap_t)); /* * create a pixbuf out of the image for downscaling */ /* first of setup a standard rgb buffer, swap bgr in same routine */ uint8_t *rgbbuf = g_malloc(buf.width*buf.height*3); for(int j=0; j<(buf.width*buf.height); j++) for(int k=0; k<3; k++) rgbbuf[3*j+k] = buf_decompressed[4*j+2-k]; /* then create pixbuf and scale down to lightmap size */ GdkPixbuf *source = gdk_pixbuf_new_from_data(rgbbuf,GDK_COLORSPACE_RGB,FALSE,8,buf.width,buf.height,(buf.width*3),NULL,NULL); GdkPixbuf *scaled = gdk_pixbuf_scale_simple(source,DT_SIMILARITY_LIGHTMAP_SIZE,DT_SIMILARITY_LIGHTMAP_SIZE,GDK_INTERP_HYPER); /* copy scaled data into lightmap */ uint8_t min=0xff,max=0; uint8_t *spixels = gdk_pixbuf_get_pixels(scaled); for(int j=0; j<(DT_SIMILARITY_LIGHTMAP_SIZE*DT_SIMILARITY_LIGHTMAP_SIZE); j++) { /* copy rgb */ for(int k=0; k<3; k++) lightmap.pixels[4*j+k] = spixels[3*j+k]; /* average intensity into 4th channel */ lightmap.pixels[4*j+3] = (lightmap.pixels[4*j+0]+ lightmap.pixels[4*j+1]+ lightmap.pixels[4*j+2])/3.0; min = MIN(min, lightmap.pixels[4*j+3]); max = MAX(max, lightmap.pixels[4*j+3]); } /* contrast stretch each channel in lightmap * TODO: do we want this... */ float scale=0; int range = max-min; if(range==0) scale = 1.0; else scale = 0xff/range; for(int j=0; j<(DT_SIMILARITY_LIGHTMAP_SIZE*DT_SIMILARITY_LIGHTMAP_SIZE); j++) { for(int k=0; k<4; k++) lightmap.pixels[4*j+k] = (lightmap.pixels[4*j+k]-min)*scale; } /* free some resources */ g_object_unref(scaled); g_object_unref(source); g_free(rgbbuf); /* store the lightmap */ dt_similarity_lightmap_store(idximg->id, &lightmap); } /* no use for buffer anymore release the mipmap */ dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); } /* update background progress */ if (jid) { fraction+=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } } while ((imgitem=g_list_next(imgitem)) && dt_control_job_get_state(job) != DT_JOB_STATE_CANCELLED); /* cleanup */ if (jid) dt_control_backgroundjobs_destroy(darktable.control, jid); } free(scratchmem); /* * Indexing opertions finished, lets reschedule the indexer * unless control is shutting down... */ if(dt_control_running()) dt_control_start_indexer(); return 0; }
int32_t dt_control_remove_images_job_run(dt_job_t *job) { long int imgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; GList *t = t1->index; int total = g_list_length(t); char message[512]= {0}; double fraction=0; snprintf(message, 512, ngettext ("removing %d image", "removing %d images", total), total ); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); sqlite3_stmt *stmt = NULL; // check that we can safely remove the image char query[1024]; gboolean remove_ok = TRUE; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT id FROM images WHERE id IN (SELECT imgid FROM selected_images) AND flags&?1=?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, DT_IMAGE_LOCAL_COPY); while(sqlite3_step(stmt) == SQLITE_ROW) { int imgid = sqlite3_column_int(stmt, 0); if (!dt_image_safe_remove(imgid)) { remove_ok = FALSE; break; } } sqlite3_finalize(stmt); if (!remove_ok) { dt_control_log(_("cannot remove local copy when the original file is not accessible.")); dt_control_backgroundjobs_destroy(darktable.control, jid); return 0; } // update remove status sprintf(query, "update images set flags = (flags | %d) where id in (select imgid from selected_images)",DT_IMAGE_REMOVE); DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), query, NULL, NULL, NULL); dt_collection_update(darktable.collection); // We need a list of files to regenerate .xmp files if there are duplicates GList *list = NULL; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select distinct folder || '/' || filename from images, film_rolls where images.film_id = film_rolls.id and images.id in (select imgid from selected_images)", -1, &stmt, NULL); while(sqlite3_step(stmt) == SQLITE_ROW) { list = g_list_append(list, g_strdup((const gchar *)sqlite3_column_text(stmt, 0))); } sqlite3_finalize(stmt); while(t) { imgid = (long int)t->data; dt_image_remove(imgid); t = g_list_delete_link(t, t); fraction=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } char *imgname; while(list) { imgname = (char *)list->data; dt_image_synch_all_xmp(imgname); list = g_list_delete_link(list, list); } dt_control_backgroundjobs_destroy(darktable.control, jid); dt_film_remove_empty(); dt_control_queue_redraw_center(); return 0; }
int32_t dt_camera_capture_job_run(dt_job_t *job) { dt_camera_capture_t *t=(dt_camera_capture_t*)job->param; int total = t->brackets ? t->count * t->brackets : t->count; char message[512]= {0}; double fraction=0; snprintf(message, 512, ngettext ("capturing %d image", "capturing %d images", total), total ); /* try to get exp program mode for nikon */ char *expprogram = (char *)dt_camctl_camera_get_property(darktable.camctl, NULL, "expprogram"); /* if fail, lets try fetching mode for cannon */ if(!expprogram) expprogram = (char *)dt_camctl_camera_get_property(darktable.camctl, NULL, "autoexposuremode"); /* Fetch all values for shutterspeed and initialize current value */ GList *values=NULL; gconstpointer orginal_value=NULL; const char *cvalue = dt_camctl_camera_get_property(darktable.camctl, NULL, "shutterspeed"); const char *value = dt_camctl_camera_property_get_first_choice(darktable.camctl, NULL, "shutterspeed"); /* get values for bracketing */ if (t->brackets && expprogram && expprogram[0]=='M' && value && cvalue) { do { // Add value to list values = g_list_append(values, g_strdup(value)); // Check if current values is the same as orginal value, then lets store item ptr if (strcmp(value,cvalue) == 0) orginal_value = g_list_last(values)->data; } while ((value = dt_camctl_camera_property_get_next_choice(darktable.camctl, NULL, "shutterspeed")) != NULL); } else { /* if this was an itended bracket capture bail out */ if(t->brackets) { dt_control_log(_("please set your camera to manual mode first!")); return 1; } } /* create the bgjob plate */ const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); GList *current_value = g_list_find(values,orginal_value); for(int i=0; i<t->count; i++) { // Delay if active if(t->delay) g_usleep(t->delay*G_USEC_PER_SEC); for(int b=0; b<(t->brackets*2)+1; b++) { // If bracket capture, lets set change shutterspeed if (t->brackets) { if (b == 0) { // First bracket, step down time with (steps*brackets), also check so we never set the longest shuttertime which would be bulb mode for(int s=0; s<(t->steps*t->brackets); s++) if (g_list_next(current_value) && g_list_next(g_list_next(current_value))) current_value = g_list_next(current_value); } else { // Step up with (steps) for(int s=0; s<t->steps; s++) if(g_list_previous(current_value)) current_value = g_list_previous(current_value); } } // set the time property for bracked capture if (t->brackets && current_value) dt_camctl_camera_set_property_string(darktable.camctl, NULL, "shutterspeed", current_value->data); // Capture image dt_camctl_camera_capture(darktable.camctl,NULL); fraction += 1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } // lets reset to orginal value before continue if (t->brackets) { current_value = g_list_find(values,orginal_value); dt_camctl_camera_set_property_string(darktable.camctl, NULL, "shutterspeed", current_value->data); } } dt_control_backgroundjobs_destroy(darktable.control, jid); // free values if(values) { for(int i=0; i<g_list_length(values); i++) g_free(g_list_nth_data(values,i)); g_list_free(values); } return 0; }
int32_t dt_camera_import_job_run(dt_job_t *job) { dt_camera_import_t *t = (dt_camera_import_t *)job->param; dt_control_log(_("starting to import images from camera")); // Setup a new filmroll to import images to.... t->film=(dt_film_t*)g_malloc(sizeof(dt_film_t)); dt_film_init(t->film); gchar* fixed_path = dt_util_fix_path(t->path); g_free(t->path); t->path = fixed_path; dt_variables_expand( t->vp, t->path, FALSE ); sprintf(t->film->dirname,"%s",dt_variables_get_result(t->vp)); dt_pthread_mutex_lock(&t->film->images_mutex); t->film->ref++; dt_pthread_mutex_unlock(&t->film->images_mutex); // Create recursive directories, abort if no access if( g_mkdir_with_parents(t->film->dirname,0755) == -1 ) { dt_control_log(_("failed to create import path `%s', import aborted."), t->film->dirname); return 1; } // Import path is ok, lets actually create the filmroll in database.. if(dt_film_new(t->film,t->film->dirname) > 0) { int total = g_list_length( t->images ); char message[512]= {0}; sprintf(message, ngettext ("importing %d image from camera", "importing %d images from camera", total), total ); t->bgj = dt_control_backgroundjobs_create(darktable.control, 0, message); // Switch to new filmroll dt_film_open(t->film->id); dt_ctl_switch_mode_to(DT_LIBRARY); // register listener dt_camctl_listener_t listener= {0}; listener.data=t; listener.image_downloaded=_camera_image_downloaded; listener.request_image_path=_camera_import_request_image_path; listener.request_image_filename=_camera_import_request_image_filename; // start download of images dt_camctl_register_listener(darktable.camctl,&listener); dt_camctl_import(darktable.camctl,t->camera,t->images,dt_conf_get_bool("plugins/capture/camera/import/delete_originals")); dt_camctl_unregister_listener(darktable.camctl,&listener); dt_control_backgroundjobs_destroy(darktable.control, t->bgj); dt_variables_params_destroy(t->vp); } else dt_control_log(_("failed to create filmroll for camera import, import aborted.")); dt_pthread_mutex_lock(&t->film->images_mutex); t->film->ref--; dt_pthread_mutex_unlock(&t->film->images_mutex); return 0; }
static int32_t dt_camera_capture_job_run(dt_job_t *job) { dt_camera_capture_t *params = dt_control_job_get_params(job); int total; char message[512]= {0}; double fraction=0; total = params->total = params->brackets ? params->count * params->brackets : params->count; snprintf(message, sizeof(message), ngettext ("capturing %d image", "capturing %d images", total), total ); pthread_mutex_init(¶ms->mutex, NULL); pthread_cond_init(¶ms->done, NULL); // register listener dt_camctl_listener_t *listener; listener = g_malloc0(sizeof(dt_camctl_listener_t)); listener->data = params; listener->image_downloaded = _camera_capture_image_downloaded; listener->request_image_path = _camera_request_image_path; listener->request_image_filename = _camera_request_image_filename; dt_camctl_register_listener(darktable.camctl, listener); /* try to get exp program mode for nikon */ char *expprogram = (char *)dt_camctl_camera_get_property(darktable.camctl, NULL, "expprogram"); /* if fail, lets try fetching mode for cannon */ if(!expprogram) expprogram = (char *)dt_camctl_camera_get_property(darktable.camctl, NULL, "autoexposuremode"); /* Fetch all values for shutterspeed and initialize current value */ GList *values=NULL; gconstpointer original_value=NULL; const char *cvalue = dt_camctl_camera_get_property(darktable.camctl, NULL, "shutterspeed"); const char *value = dt_camctl_camera_property_get_first_choice(darktable.camctl, NULL, "shutterspeed"); /* get values for bracketing */ if (params->brackets && expprogram && expprogram[0]=='M' && value && cvalue) { do { // Add value to list values = g_list_append(values, g_strdup(value)); // Check if current values is the same as original value, then lets store item ptr if (strcmp(value,cvalue) == 0) original_value = g_list_last(values)->data; } while ((value = dt_camctl_camera_property_get_next_choice(darktable.camctl, NULL, "shutterspeed")) != NULL); } else { /* if this was an intended bracket capture bail out */ if(params->brackets) { dt_control_log(_("please set your camera to manual mode first!")); pthread_mutex_lock(¶ms->mutex); pthread_cond_wait(¶ms->done, ¶ms->mutex); pthread_mutex_unlock(¶ms->mutex); pthread_mutex_destroy(¶ms->mutex); pthread_cond_destroy(¶ms->done); dt_import_session_destroy(params->shared.session); dt_camctl_unregister_listener(darktable.camctl, listener); g_free(listener); free(params); return 1; } } /* create the bgjob plate */ const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); GList *current_value = g_list_find(values,original_value); for(uint32_t i=0; i < params->count; i++) { // Delay if active if(params->delay) g_usleep(params->delay*G_USEC_PER_SEC); for(uint32_t b=0; b < (params->brackets*2)+1; b++) { // If bracket capture, lets set change shutterspeed if (params->brackets) { if (b == 0) { // First bracket, step down time with (steps*brackets), also check so we never set the longest shuttertime which would be bulb mode for(uint32_t s=0; s < (params->steps * params->brackets); s++) if (g_list_next(current_value) && g_list_next(g_list_next(current_value))) current_value = g_list_next(current_value); } else { // Step up with (steps) for(uint32_t s=0; s < params->steps; s++) if(g_list_previous(current_value)) current_value = g_list_previous(current_value); } } // set the time property for bracket capture if (params->brackets && current_value) dt_camctl_camera_set_property_string(darktable.camctl, NULL, "shutterspeed", current_value->data); // Capture image dt_camctl_camera_capture(darktable.camctl,NULL); fraction += 1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } // lets reset to original value before continue if (params->brackets) { current_value = g_list_find(values,original_value); dt_camctl_camera_set_property_string(darktable.camctl, NULL, "shutterspeed", current_value->data); } } /* wait for last image capture before exiting job */ pthread_mutex_lock(¶ms->mutex); pthread_cond_wait(¶ms->done, ¶ms->mutex); pthread_mutex_unlock(¶ms->mutex); pthread_mutex_destroy(¶ms->mutex); pthread_cond_destroy(¶ms->done); /* cleanup */ dt_control_backgroundjobs_destroy(darktable.control, jid); dt_import_session_destroy(params->shared.session); dt_camctl_unregister_listener(darktable.camctl, listener); g_free(listener); // free values if(values) { g_list_free_full(values, g_free); } free(params); return 0; }
int32_t dt_control_delete_images_job_run(dt_job_t *job) { int imgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; GList *t = t1->index; char *imgs = _get_image_list(t); int total = g_list_length(t); char message[512]= {0}; double fraction=0; snprintf(message, 512, ngettext ("deleting %d image", "deleting %d images", total), total ); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); sqlite3_stmt *stmt; _set_remove_flag(imgs); dt_collection_update(darktable.collection); // We need a list of files to regenerate .xmp files if there are duplicates GList *list = _get_full_pathname(imgs); free(imgs); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "select count(id) from images where filename in (select filename from images where id = ?1) and film_id in (select film_id from images where id = ?1)", -1, &stmt, NULL); while(t) { imgid = GPOINTER_TO_INT(t->data); char filename[DT_MAX_PATH_LEN]; gboolean from_cache = FALSE; dt_image_full_path(imgid, filename, DT_MAX_PATH_LEN, &from_cache); int duplicates = 0; DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); if(sqlite3_step(stmt) == SQLITE_ROW) duplicates = sqlite3_column_int(stmt, 0); sqlite3_reset(stmt); sqlite3_clear_bindings(stmt); // remove from disk: if(duplicates == 1) { // there are no further duplicates so we can remove the source data file (void)g_unlink(filename); dt_image_remove(imgid); // all sidecar files - including left-overs - can be deleted; // left-overs can result when previously duplicates have been REMOVED; // no need to keep them as the source data file is gone. const int len = DT_MAX_PATH_LEN + 30; gchar pattern[len]; // NULL terminated list of glob patterns; should include "" and can be extended if needed static const gchar *glob_patterns[] = { "", "_[0-9][0-9]", "_[0-9][0-9][0-9]", "_[0-9][0-9][0-9][0-9]", NULL }; const gchar **glob_pattern = glob_patterns; GList *files = NULL; while(*glob_pattern) { snprintf(pattern, len, "%s", filename); gchar *c1 = pattern + strlen(pattern); while(*c1 != '.' && c1 > pattern) c1--; snprintf(c1, pattern + len - c1, "%s", *glob_pattern); const gchar *c2 = filename + strlen(filename); while(*c2 != '.' && c2 > filename) c2--; snprintf(c1+strlen(*glob_pattern), pattern + len - c1 - strlen(*glob_pattern), "%s.xmp", c2); #ifdef __WIN32__ WIN32_FIND_DATA data; HANDLE handle = FindFirstFile(pattern, &data); if(handle != INVALID_HANDLE_VALUE) { do files = g_list_append(files, g_strdup(data.cFileName)); while(FindNextFile(handle, &data)); } #else glob_t globbuf; if(!glob(pattern, 0, NULL, &globbuf)) { for(size_t i=0; i < globbuf.gl_pathc; i++) files = g_list_append(files, g_strdup(globbuf.gl_pathv[i])); globfree(&globbuf); } #endif glob_pattern++; } GList *file_iter = g_list_first(files); while(file_iter != NULL) { (void)g_unlink(file_iter->data); file_iter = g_list_next(file_iter); } g_list_free_full(files, g_free); } else { // don't remove the actual source data if there are further duplicates using it; // just delete the xmp file of the duplicate selected. dt_image_path_append_version(imgid, filename, DT_MAX_PATH_LEN); char *c = filename + strlen(filename); sprintf(c, ".xmp"); dt_image_remove(imgid); (void)g_unlink(filename); } t = g_list_delete_link(t, t); fraction=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } sqlite3_finalize(stmt); char *imgname; while(list) { imgname = (char *)list->data; dt_image_synch_all_xmp(imgname); list = g_list_delete_link(list, list); } g_list_free(list); dt_control_backgroundjobs_destroy(darktable.control, jid); dt_film_remove_empty(); dt_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_CHANGED); dt_control_queue_redraw_center(); return 0; }
int32_t dt_control_remove_images_job_run(dt_job_t *job) { int imgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; GList *t = t1->index; char *imgs = _get_image_list(t); int total = g_list_length(t); char message[512]= {0}; double fraction=0; snprintf(message, 512, ngettext ("removing %d image", "removing %d images", total), total ); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 0, message); sqlite3_stmt *stmt = NULL; // check that we can safely remove the image gboolean remove_ok = TRUE; DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT id FROM images WHERE id IN (?2) AND flags&?1=?1", -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, DT_IMAGE_LOCAL_COPY); DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 2, imgs, -1, SQLITE_STATIC); while(sqlite3_step(stmt) == SQLITE_ROW) { int imgid = sqlite3_column_int(stmt, 0); if (!dt_image_safe_remove(imgid)) { remove_ok = FALSE; break; } } sqlite3_finalize(stmt); if (!remove_ok) { dt_control_log(_("cannot remove local copy when the original file is not accessible.")); dt_control_backgroundjobs_destroy(darktable.control, jid); free(imgs); return 0; } // update remove status _set_remove_flag(imgs); dt_collection_update(darktable.collection); // We need a list of files to regenerate .xmp files if there are duplicates GList *list = _get_full_pathname(imgs); free(imgs); while(t) { imgid = GPOINTER_TO_INT(t->data); dt_image_remove(imgid); t = g_list_delete_link(t, t); fraction=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); } char *imgname; while(list) { imgname = (char *)list->data; dt_image_synch_all_xmp(imgname); list = g_list_delete_link(list, list); } dt_control_backgroundjobs_destroy(darktable.control, jid); dt_film_remove_empty(); dt_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_CHANGED); dt_control_queue_redraw_center(); return 0; }
int32_t dt_control_merge_hdr_job_run(dt_job_t *job) { long int imgid = -1; dt_control_image_enumerator_t *t1 = (dt_control_image_enumerator_t *)job->param; GList *t = t1->index; int total = g_list_length(t); char message[512]= {0}; double fraction=0; snprintf(message, 512, ngettext ("merging %d image", "merging %d images", total), total ); const guint *jid = dt_control_backgroundjobs_create(darktable.control, 1, message); float *pixels = NULL; float *weight = NULL; int wd = 0, ht = 0, first_imgid = -1; uint32_t filter = 0; float whitelevel = 0.0f; total ++; while(t) { imgid = (long int)t->data; dt_mipmap_buffer_t buf; dt_mipmap_cache_read_get(darktable.mipmap_cache, &buf, imgid, DT_MIPMAP_FULL, DT_MIPMAP_BLOCKING); // just take a copy. also do it after blocking read, so filters and bpp will make sense. const dt_image_t *img = dt_image_cache_read_get(darktable.image_cache, imgid); dt_image_t image = *img; dt_image_cache_read_release(darktable.image_cache, img); if(image.filters == 0 || image.bpp != sizeof(uint16_t)) { dt_control_log(_("exposure bracketing only works on raw images")); dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); free(pixels); free(weight); goto error; } filter = dt_image_flipped_filter(img); if(buf.size != DT_MIPMAP_FULL) { dt_control_log(_("failed to get raw buffer from image `%s'"), image.filename); dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); free(pixels); free(weight); goto error; } if(!pixels) { first_imgid = imgid; pixels = (float *)malloc(sizeof(float)*image.width*image.height); weight = (float *)malloc(sizeof(float)*image.width*image.height); memset(pixels, 0x0, sizeof(float)*image.width*image.height); memset(weight, 0x0, sizeof(float)*image.width*image.height); wd = image.width; ht = image.height; } else if(image.width != wd || image.height != ht) { dt_control_log(_("images have to be of same size!")); free(pixels); free(weight); dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); goto error; } // if no valid exif data can be found, assume peleng fisheye at f/16, 8mm, with half of the light lost in the system => f/22 const float eap = image.exif_aperture > 0.0f ? image.exif_aperture : 22.0f; const float efl = image.exif_focal_length > 0.0f ? image.exif_focal_length : 8.0f; const float rad = .5f * efl/eap; const float aperture = M_PI * rad * rad; const float iso = image.exif_iso > 0.0f ? image.exif_iso : 100.0f; const float exp = image.exif_exposure > 0.0f ? image.exif_exposure : 1.0f; const float cal = 100.0f/(aperture*exp*iso); whitelevel = fmaxf(whitelevel, cal); #ifdef _OPENMP #pragma omp parallel for schedule(static) default(none) shared(buf, pixels, weight, wd, ht) #endif for(int k=0; k<wd*ht; k++) { const uint16_t in = ((uint16_t *)buf.buf)[k]; const float w = .001f + (in >= 1000 ? (in < 65000 ? in/65000.0f : 0.0f) : exp * 0.01f); pixels[k] += w * in * cal; weight[k] += w; } t = g_list_delete_link(t, t); /* update backgroundjob ui plate */ fraction+=1.0/total; dt_control_backgroundjobs_progress(darktable.control, jid, fraction); dt_mipmap_cache_read_release(darktable.mipmap_cache, &buf); } // normalize by white level to make clipping at 1.0 work as expected (to be sure, scale down one more stop, thus the 0.5): #ifdef _OPENMP #pragma omp parallel for schedule(static) default(none) shared(pixels, wd, ht, weight, whitelevel) #endif for(int k=0; k<wd*ht; k++) pixels[k] = fmaxf(0.0f, fminf(2.0f, pixels[k]/((.5f*whitelevel*65535.0f)*weight[k]))); // output hdr as digital negative with exif data. uint8_t exif[65535]; char pathname[1024]; dt_image_full_path(first_imgid, pathname, 1024); const int exif_len = dt_exif_read_blob(exif, pathname, 0, first_imgid); char *c = pathname + strlen(pathname); while(*c != '.' && c > pathname) c--; g_strlcpy(c, "-hdr.dng", sizeof(pathname)-(c-pathname)); dt_imageio_write_dng(pathname, pixels, wd, ht, exif, exif_len, filter, whitelevel); dt_control_backgroundjobs_progress(darktable.control, jid, 1.0f); while(*c != '/' && c > pathname) c--; dt_control_log(_("wrote merged hdr `%s'"), c+1); // import new image gchar *directory = g_path_get_dirname((const gchar *)pathname); dt_film_t film; const int filmid = dt_film_new(&film, directory); dt_image_import(filmid, pathname, TRUE); g_free (directory); free(pixels); free(weight); error: dt_control_backgroundjobs_destroy(darktable.control, jid); return 0; }