diff --git a/src/common/imageio_rawspeed.cc b/src/common/imageio_rawspeed.cc index 5a63fff5e8f1..1d55557a5b76 100644 --- a/src/common/imageio_rawspeed.cc +++ b/src/common/imageio_rawspeed.cc @@ -132,6 +132,86 @@ static gboolean _ignore_image(const gchar *filename) return FALSE; } +static void load_image_metadata(const RawImage &r, dt_image_t *img) +{ + g_strlcpy(img->camera_maker, r.metadata.canonical_make.c_str(), sizeof(img->camera_maker)); + g_strlcpy(img->camera_model, r.metadata.canonical_model.c_str(), sizeof(img->camera_model)); + g_strlcpy(img->camera_alias, r.metadata.canonical_alias.c_str(), sizeof(img->camera_alias)); + dt_image_refresh_makermodel(img); + + // We used to partial match the Canon local rebrandings so lets pass on + // the value just in those cases to be able to fix old history stacks + static const struct + { + const char *mungedname; + const char *origname; + } legacy_aliases[] = { + { "Canon EOS", "Canon EOS REBEL SL1" }, + { "Canon EOS", "Canon EOS Kiss X7" }, + { "Canon EOS", "Canon EOS DIGITAL REBEL XT" }, + { "Canon EOS", "Canon EOS Kiss Digital N" }, + { "Canon EOS", "Canon EOS 350D" }, + { "Canon EOS", "Canon EOS DIGITAL REBEL XSi" }, + { "Canon EOS", "Canon EOS Kiss Digital X2" }, + { "Canon EOS", "Canon EOS Kiss X2" }, + { "Canon EOS", "Canon EOS REBEL T5i" }, + { "Canon EOS", "Canon EOS Kiss X7i" }, + { "Canon EOS", "Canon EOS Rebel T6i" }, + { "Canon EOS", "Canon EOS Kiss X8i" }, + { "Canon EOS", "Canon EOS Rebel T6s" }, + { "Canon EOS", "Canon EOS 8000D" }, + { "Canon EOS", "Canon EOS REBEL T1i" }, + { "Canon EOS", "Canon EOS Kiss X3" }, + { "Canon EOS", "Canon EOS REBEL T2i" }, + { "Canon EOS", "Canon EOS Kiss X4" }, + { "Canon EOS REBEL T3", "Canon EOS REBEL T3i" }, + { "Canon EOS", "Canon EOS Kiss X5" }, + { "Canon EOS", "Canon EOS REBEL T4i" }, + { "Canon EOS", "Canon EOS Kiss X6i" }, + { "Canon EOS", "Canon EOS DIGITAL REBEL XS" }, + { "Canon EOS", "Canon EOS Kiss Digital F" }, + { "Canon EOS", "Canon EOS REBEL T5" }, + { "Canon EOS", "Canon EOS Kiss X70" }, + { "Canon EOS", "Canon EOS DIGITAL REBEL XTi" }, + { "Canon EOS", "Canon EOS Kiss Digital X" }, + }; + + for(uint32_t i = 0; i < (sizeof(legacy_aliases) / sizeof(legacy_aliases[1])); i++) + { + if(!strcmp(legacy_aliases[i].origname, r.metadata.model.c_str())) + { + g_strlcpy(img->camera_legacy_makermodel, legacy_aliases[i].mungedname, sizeof(img->camera_legacy_makermodel)); + break; + } + } + + // Grab the WB + for(int i = 0; i < 4; i++) img->wb_coeffs[i] = r.metadata.wbCoeffs[i]; + + const int msize = r.metadata.colorMatrix.size(); + // Grab the adobe coeff + for(int k = 0; k < 4; k++) + for(int i = 0; i < 3; i++) + { + const int idx = k * 3 + i; + if(idx < msize) + img->adobe_XYZ_to_CAM[k][i] = (float)r.metadata.colorMatrix[idx] / (float)ADOBE_COEFF_FACTOR; + else + img->adobe_XYZ_to_CAM[k][i] = 0.0f; + } + + // FIXME: grab r->metadata.colorMatrix. + + img->fuji_rotation_pos = r.metadata.fujiRotationPos; + img->pixel_aspect_ratio = (float)r.metadata.pixelAspectRatio; + + + // Check if the camera is missing samples + const Camera *cam = meta->getCamera(r.metadata.make.c_str(), r.metadata.model.c_str(), r.metadata.mode.c_str()); + + if(cam && cam->supportStatus == Camera::SupportStatus::NoSamples) img->camera_missing_sample = TRUE; +} + dt_imageio_retval_t dt_imageio_open_rawspeed(dt_image_t *img, const char *filename, dt_mipmap_buffer_t *mbuf) { @@ -141,21 +221,18 @@ dt_imageio_retval_t dt_imageio_open_rawspeed(dt_image_t *img, const char *filena char filen[PATH_MAX] = { 0 }; snprintf(filen, sizeof(filen), "%s", filename); - FileReader f(filen); - - std::unique_ptr d; - std::unique_ptr m; + FileReader file(filen); try { dt_rawspeed_load_meta(); dt_pthread_mutex_lock(&darktable.readFile_mutex); - m = f.readFile(); + auto m = file.readFile(); dt_pthread_mutex_unlock(&darktable.readFile_mutex); RawParser t(*m.get()); - d = t.getDecoder(meta); + auto d = t.getDecoder(meta); if(!d.get()) return DT_IMAGEIO_FILE_CORRUPTED; @@ -164,74 +241,88 @@ dt_imageio_retval_t dt_imageio_open_rawspeed(dt_image_t *img, const char *filena d->decodeRaw(); d->decodeMetaData(meta); RawImage r = d->mRaw; + bool isMultiFrame{false}; - const auto errors = r->getErrors(); - for(const auto &error : errors) - fprintf(stderr, "[rawspeed] (%s) %s\n", img->filename, error.c_str()); - - g_strlcpy(img->camera_maker, r->metadata.canonical_make.c_str(), sizeof(img->camera_maker)); - g_strlcpy(img->camera_model, r->metadata.canonical_model.c_str(), sizeof(img->camera_model)); - g_strlcpy(img->camera_alias, r->metadata.canonical_alias.c_str(), sizeof(img->camera_alias)); - dt_image_refresh_makermodel(img); - - // We used to partial match the Canon local rebrandings so lets pass on - // the value just in those cases to be able to fix old history stacks - static const struct { - const char *mungedname; - const char *origname; - } legacy_aliases[] = { - {"Canon EOS","Canon EOS REBEL SL1"}, - {"Canon EOS","Canon EOS Kiss X7"}, - {"Canon EOS","Canon EOS DIGITAL REBEL XT"}, - {"Canon EOS","Canon EOS Kiss Digital N"}, - {"Canon EOS","Canon EOS 350D"}, - {"Canon EOS","Canon EOS DIGITAL REBEL XSi"}, - {"Canon EOS","Canon EOS Kiss Digital X2"}, - {"Canon EOS","Canon EOS Kiss X2"}, - {"Canon EOS","Canon EOS REBEL T5i"}, - {"Canon EOS","Canon EOS Kiss X7i"}, - {"Canon EOS","Canon EOS Rebel T6i"}, - {"Canon EOS","Canon EOS Kiss X8i"}, - {"Canon EOS","Canon EOS Rebel T6s"}, - {"Canon EOS","Canon EOS 8000D"}, - {"Canon EOS","Canon EOS REBEL T1i"}, - {"Canon EOS","Canon EOS Kiss X3"}, - {"Canon EOS","Canon EOS REBEL T2i"}, - {"Canon EOS","Canon EOS Kiss X4"}, - {"Canon EOS REBEL T3","Canon EOS REBEL T3i"}, - {"Canon EOS","Canon EOS Kiss X5"}, - {"Canon EOS","Canon EOS REBEL T4i"}, - {"Canon EOS","Canon EOS Kiss X6i"}, - {"Canon EOS","Canon EOS DIGITAL REBEL XS"}, - {"Canon EOS","Canon EOS Kiss Digital F"}, - {"Canon EOS","Canon EOS REBEL T5"}, - {"Canon EOS","Canon EOS Kiss X70"}, - {"Canon EOS","Canon EOS DIGITAL REBEL XTi"}, - {"Canon EOS","Canon EOS Kiss Digital X"}, - }; - - for(uint32_t i = 0; i < (sizeof(legacy_aliases) / sizeof(legacy_aliases[1])); i++) - if(!strcmp(legacy_aliases[i].origname, r->metadata.model.c_str())) + /* free auto pointers on spot */ + d.reset(); + m.reset(); + + + for(const auto &frame : r) + { + const auto errors = frame->getErrors(); + for(const auto &error : errors) { - g_strlcpy(img->camera_legacy_makermodel, legacy_aliases[i].mungedname, sizeof(img->camera_legacy_makermodel)); - break; + fprintf(stderr, "[rawspeed] (%s) %s\n", img->filename, error.c_str()); } + } + + // Get DefaultUserCrop + if(img->flags & DT_IMAGE_HAS_USERCROP) dt_exif_img_check_usercrop(img, filename); - img->raw_black_level = r->blackLevel; - img->raw_white_point = r->whitePoint; + load_image_metadata(r, img); - if(r->blackLevelSeparate[0] == -1 - || r->blackLevelSeparate[1] == -1 - || r->blackLevelSeparate[2] == -1 - || r->blackLevelSeparate[3] == -1) + if(r.size() > 1) { - r->calculateBlackAreas(); + auto isCFA = r.get(0)->isCFA; + for(const auto &f : r) + { + if(f->isCFA != isCFA) + { + fprintf(stderr, "[rawspeed] (%s) inconsistent CFA flag across frames\n", img->filename); + return DT_IMAGEIO_FILE_CORRUPTED; + } + } + isMultiFrame = isCFA; + } + + img->raw_black_level = r.get(0)->blackLevel; + img->raw_white_point = r.get(0)->whitePoint; + if(isMultiFrame) + { + for(size_t i=0;i< r.size();++i) + { + if(r.get(i)->blackLevel != img->raw_black_level) + { + fprintf(stderr, "[rawspeed] (%s) inconsistent black levels across frames\n", img->filename); + } + if(static_cast(r.get(i)->whitePoint) != img->raw_white_point) + { + fprintf(stderr, "[rawspeed] (%s) inconsistent white points across frames\n", img->filename); + } + } + } + + for(const auto &f : r) + { + if(f->blackLevelSeparate[0] == -1 + || f->blackLevelSeparate[1] == -1 + || f->blackLevelSeparate[2] == -1 + || f->blackLevelSeparate[3] == -1) + { + f->calculateBlackAreas(); + } } for(uint8_t i = 0; i < 4; i++) - img->raw_black_level_separate[i] = r->blackLevelSeparate[i]; + { + img->raw_black_level_separate[i] = r.get(0)->blackLevelSeparate[i]; + } + if(isMultiFrame) + { + for(const auto &f : r) + { + for(uint8_t i = 0; i < 4; i++) + { + if(img->raw_black_level_separate[i] != f->blackLevelSeparate[i]) + { + fprintf(stderr, "[rawspeed] (%s) inconsistent separate black levels across frames\n", img->filename); + } + } + } + } - if(r->blackLevel == -1) + if(r.get(0)->blackLevel == -1) { float black = 0.0f; for(uint8_t i = 0; i < 4; i++) @@ -249,34 +340,22 @@ dt_imageio_retval_t dt_imageio_open_rawspeed(dt_image_t *img, const char *filena * ??? */ - /* free auto pointers on spot */ - d.reset(); - m.reset(); - - // Grab the WB - for(int i = 0; i < 4; i++) - img->wb_coeffs[i] = r->metadata.wbCoeffs[i]; - const int msize = r->metadata.colorMatrix.size(); - // Grab the adobe coeff - for(int k = 0; k < 4; k++) - for(int i = 0; i < 3; i++) + if(isMultiFrame) + { + auto type = r.get(0)->getDataType(); + for(const auto &f : r) { - const int idx = k*3 + i; - if(idx < msize) - img->adobe_XYZ_to_CAM[k][i] = - (float)r->metadata.colorMatrix[idx] / (float)ADOBE_COEFF_FACTOR; - else - img->adobe_XYZ_to_CAM[k][i] = 0.0f; + if(f->getDataType() != type) + { + fprintf(stderr, "[rawspeed] (%s) inconsistent data types across frames\n", img->filename); + return DT_IMAGEIO_FILE_CORRUPTED; + } } + } - // FIXME: grab r->metadata.colorMatrix. - - // Get DefaultUserCrop - if (img->flags & DT_IMAGE_HAS_USERCROP) - dt_exif_img_check_usercrop(img, filename); - if(r->getDataType() == TYPE_FLOAT32) + if(r.get(0)->getDataType() == TYPE_FLOAT32) { img->flags |= DT_IMAGE_HDR; @@ -285,31 +364,47 @@ dt_imageio_retval_t dt_imageio_open_rawspeed(dt_image_t *img, const char *filena for(int k = 0; k < 4; k++) img->buf_dsc.processed_maximum[k] = 1.0f; } + // we don't support multiframe foll color images now img->buf_dsc.filters = 0u; - if(!r->isCFA) + if(!r.get(0)->isCFA) { const dt_imageio_retval_t ret = dt_imageio_open_rawspeed_sraw(img, r, mbuf); return ret; } - if((r->getDataType() != TYPE_USHORT16) && (r->getDataType() != TYPE_FLOAT32)) + if(isMultiFrame) + { + auto bpp = r.get(0)->getBpp(); + for(const auto &f : r) + { + if(f->getBpp() != bpp) + { + fprintf(stderr, "[rawspeed] (%s) inconsistent bit depth across frames\n", img->filename); + return DT_IMAGEIO_FILE_CORRUPTED; + } + } + } + + //all frames should have data type and bit depth consistent, so we only check first frame + if((r.get(0)->getDataType() != TYPE_USHORT16) && (r.get(0)->getDataType() != TYPE_FLOAT32)) return DT_IMAGEIO_FILE_CORRUPTED; - if((r->getBpp() != sizeof(uint16_t)) && (r->getBpp() != sizeof(float))) + if((r.get(0)->getBpp() != sizeof(uint16_t)) && (r.get(0)->getBpp() != sizeof(float))) return DT_IMAGEIO_FILE_CORRUPTED; - if((r->getDataType() == TYPE_USHORT16) && (r->getBpp() != sizeof(uint16_t))) + if((r.get(0)->getDataType() == TYPE_USHORT16) && (r.get(0)->getBpp() != sizeof(uint16_t))) return DT_IMAGEIO_FILE_CORRUPTED; - if((r->getDataType() == TYPE_FLOAT32) && (r->getBpp() != sizeof(float))) + if((r.get(0)->getDataType() == TYPE_FLOAT32) && (r.get(0)->getBpp() != sizeof(float))) return DT_IMAGEIO_FILE_CORRUPTED; - const float cpp = r->getCpp(); + const auto cpp = r.get(0)->getCpp(); if(cpp != 1) return DT_IMAGEIO_FILE_CORRUPTED; img->buf_dsc.channels = 1; + img->buf_dsc.frames = r.size(); - switch(r->getBpp()) + switch(r.get(0)->getBpp()) { case sizeof(uint16_t): img->buf_dsc.datatype = TYPE_UINT16; @@ -322,15 +417,27 @@ dt_imageio_retval_t dt_imageio_open_rawspeed(dt_image_t *img, const char *filena } // dimensions of uncropped image - iPoint2D dimUncropped = r->getUncroppedDim(); + iPoint2D dimUncropped = r.get(0)->getUncroppedDim(); img->width = dimUncropped.x; img->height = dimUncropped.y; + if(isMultiFrame) + { + for(const auto &f : r) + { + if((img->width != f->getUncroppedDim().x) || (img->height != f->getUncroppedDim().y)) + { + fprintf(stderr, "[rawspeed] (%s) inconsistent uncropped dimensions across frames\n", img->filename); + return DT_IMAGEIO_FILE_CORRUPTED; + } + } + } + // dimensions of cropped image - iPoint2D dimCropped = r->dim; + iPoint2D dimCropped = r.get(0)->dim; // crop - Top,Left corner - iPoint2D cropTL = r->getCropOffset(); + iPoint2D cropTL = r.get(0)->getCropOffset(); img->crop_x = cropTL.x; img->crop_y = cropTL.y; @@ -339,12 +446,27 @@ dt_imageio_retval_t dt_imageio_open_rawspeed(dt_image_t *img, const char *filena img->crop_width = cropBR.x; img->crop_height = cropBR.y; - img->fuji_rotation_pos = r->metadata.fujiRotationPos; - img->pixel_aspect_ratio = (float)r->metadata.pixelAspectRatio; + if(isMultiFrame) + { + for(const auto &f : r) + { + if(dimCropped != f->dim) + { + fprintf(stderr, "[rawspeed] (%s) inconsistent cropped dimensions across frames\n", img->filename); + return DT_IMAGEIO_FILE_CORRUPTED; + } + if(cropTL != f->getCropOffset()) + { + fprintf(stderr, "[rawspeed] (%s) inconsistent crop offset across frames\n", img->filename); + return DT_IMAGEIO_FILE_CORRUPTED; + } + } + } + // as the X-Trans filters comments later on states, these are for // cropped image, so we need to uncrop them. - img->buf_dsc.filters = dt_rawspeed_crop_dcraw_filters(r->cfa.getDcrawFilter(), cropTL.x, cropTL.y); + img->buf_dsc.filters = dt_rawspeed_crop_dcraw_filters(r.get(0)->cfa.getDcrawFilter(), cropTL.x, cropTL.y); if(FILTERS_ARE_4BAYER(img->buf_dsc.filters)) img->flags |= DT_IMAGE_4BAYER; @@ -365,7 +487,7 @@ dt_imageio_retval_t dt_imageio_open_rawspeed(dt_image_t *img, const char *filena for(int i = 0; i < 6; ++i) for(int j = 0; j < 6; ++j) { - img->buf_dsc.xtrans[j][i] = (uint8_t)r->cfa.getColorAt(i % 6, j % 6); + img->buf_dsc.xtrans[j][i] = (uint8_t)r.get(0)->cfa.getColorAt(i % 6, j % 6); } } } @@ -388,25 +510,50 @@ dt_imageio_retval_t dt_imageio_open_rawspeed(dt_image_t *img, const char *filena * (from Klaus: r->pitch may differ from DT pitch (line to line spacing)) * else fallback to generic dt_imageio_flip_buffers() */ - const size_t bufSize_mipmap = (size_t)img->width * img->height * r->getBpp(); - const size_t bufSize_rawspeed = (size_t)r->pitch * dimUncropped.y; - if(bufSize_mipmap == bufSize_rawspeed) + if(isMultiFrame) { - memcpy(buf, r->getDataUncropped(0, 0), bufSize_mipmap); + /*for(size_t f = 0; f < r.size(); ++f) + { + char *ptr = (char *)r.get(f)->getDataUncropped(0, 0); + int value = (((float)f+1) / ((float)(r.size()+2))) * img->raw_white_point; + for(int j = 0; j < dimUncropped.y; ++j) + { + uint16_t * start = (uint16_t*)(ptr + (j * r.get(f)->pitch)); + for(int i=0;iwidth * img->height * img->buf_dsc.frames * r.get(0)->getBpp(); + const size_t bufSize_rawspeed = (size_t)r.get(0)->pitch * dimUncropped.y * r.size(); + const size_t frame_size = bufSize_mipmap / r.size(); + fprintf(stderr,"[rawspeed] size mipmap: %lu, size raw: %lu, size frame: %lu\n",bufSize_mipmap,bufSize_rawspeed,frame_size); + for(size_t i = 0; i < r.size(); ++i) + { + fprintf(stderr,"[rawspeed] frame %lu, %p\n",i, (void*)((char *)buf + (i*frame_size))); + dt_imageio_flip_buffers(((char *)buf) + (i * frame_size), (char *)r.get(i)->getDataUncropped(0, 0), + r.get(i)->getBpp(), dimUncropped.x, dimUncropped.y, dimUncropped.x, dimUncropped.y, + r.get(i)->pitch, ORIENTATION_NONE); + } } else { - dt_imageio_flip_buffers((char *)buf, (char *)r->getDataUncropped(0, 0), r->getBpp(), dimUncropped.x, - dimUncropped.y, dimUncropped.x, dimUncropped.y, r->pitch, ORIENTATION_NONE); + const size_t bufSize_mipmap = (size_t)img->width * img->height * r.get(0)->getBpp(); + const size_t bufSize_rawspeed = (size_t)r.get(0)->pitch * dimUncropped.y; + if(bufSize_mipmap == bufSize_rawspeed) + { + memcpy(buf, r.get(0)->getDataUncropped(0, 0), bufSize_mipmap); + } + else + { + dt_imageio_flip_buffers((char *)buf, (char *)r.get(0)->getDataUncropped(0, 0), r.get(0)->getBpp(), + dimUncropped.x, dimUncropped.y, dimUncropped.x, dimUncropped.y, r.get(0)->pitch, + ORIENTATION_NONE); + } } - // Check if the camera is missing samples - const Camera *cam = meta->getCamera(r->metadata.make.c_str(), - r->metadata.model.c_str(), - r->metadata.mode.c_str()); - if(cam && cam->supportStatus == Camera::SupportStatus::NoSamples) - img->camera_missing_sample = TRUE; } catch(const std::exception &exc) { @@ -434,17 +581,17 @@ dt_imageio_retval_t dt_imageio_open_rawspeed_sraw(dt_image_t *img, RawImage r, d img->flags &= ~DT_IMAGE_LDR; img->flags &= ~DT_IMAGE_RAW; img->flags |= DT_IMAGE_S_RAW; - img->width = r->dim.x; - img->height = r->dim.y; + img->width = r.get(0)->dim.x; + img->height = r.get(0)->dim.y; // actually we want to store full floats here: img->buf_dsc.channels = 4; img->buf_dsc.datatype = TYPE_FLOAT; - if(r->getDataType() != TYPE_USHORT16 && r->getDataType() != TYPE_FLOAT32) + if(r.get(0)->getDataType() != TYPE_USHORT16 && r.get(0)->getDataType() != TYPE_FLOAT32) return DT_IMAGEIO_FILE_CORRUPTED; - const uint32_t cpp = r->getCpp(); + const uint32_t cpp = r.get(0)->getCpp(); if(cpp != 1 && cpp != 3 && cpp != 4) return DT_IMAGEIO_FILE_CORRUPTED; // if buf is NULL, we quit the fct here @@ -467,14 +614,14 @@ dt_imageio_retval_t dt_imageio_open_rawspeed_sraw(dt_image_t *img, RawImage r, d * we need to copy data from only channel to each of 3 channels */ - if(r->getDataType() == TYPE_USHORT16) + if(r.get(0)->getDataType() == TYPE_USHORT16) { #ifdef _OPENMP #pragma omp parallel for default(none) schedule(static) dt_omp_firstprivate(cpp) shared(r, img, buf) #endif for(int j = 0; j < img->height; j++) { - const uint16_t *in = (uint16_t *)r->getData(0, j); + const uint16_t *in = (uint16_t *)r.get(0)->getData(0, j); float *out = ((float *)buf) + (size_t)4 * j * img->width; for(int i = 0; i < img->width; i++, in += cpp, out += 4) @@ -493,7 +640,7 @@ dt_imageio_retval_t dt_imageio_open_rawspeed_sraw(dt_image_t *img, RawImage r, d #endif for(int j = 0; j < img->height; j++) { - const float *in = (float *)r->getData(0, j); + const float *in = (float *)r.get(0)->getData(0, j); float *out = ((float *)buf) + (size_t)4 * j * img->width; for(int i = 0; i < img->width; i++, in += cpp, out += 4) @@ -513,14 +660,14 @@ dt_imageio_retval_t dt_imageio_open_rawspeed_sraw(dt_image_t *img, RawImage r, d * just copy 3 ch to 3 ch */ - if(r->getDataType() == TYPE_USHORT16) + if(r.get(0)->getDataType() == TYPE_USHORT16) { #ifdef _OPENMP #pragma omp parallel for default(none) schedule(static) dt_omp_firstprivate(cpp) shared(r, img, buf) #endif for(int j = 0; j < img->height; j++) { - const uint16_t *in = (uint16_t *)r->getData(0, j); + const uint16_t *in = (uint16_t *)r.get(0)->getData(0, j); float *out = ((float *)buf) + (size_t)4 * j * img->width; for(int i = 0; i < img->width; i++, in += cpp, out += 4) @@ -539,7 +686,7 @@ dt_imageio_retval_t dt_imageio_open_rawspeed_sraw(dt_image_t *img, RawImage r, d #endif for(int j = 0; j < img->height; j++) { - const float *in = (float *)r->getData(0, j); + const float *in = (float *)r.get(0)->getData(0, j); float *out = ((float *)buf) + (size_t)4 * j * img->width; for(int i = 0; i < img->width; i++, in += cpp, out += 4) @@ -557,9 +704,9 @@ dt_imageio_retval_t dt_imageio_open_rawspeed_sraw(dt_image_t *img, RawImage r, d img->loader = LOADER_RAWSPEED; // Check if the camera is missing samples - const Camera *cam = meta->getCamera(r->metadata.make.c_str(), - r->metadata.model.c_str(), - r->metadata.mode.c_str()); + const Camera *cam = meta->getCamera(r.metadata.make.c_str(), + r.metadata.model.c_str(), + r.metadata.mode.c_str()); if(cam && cam->supportStatus == Camera::SupportStatus::NoSamples) img->camera_missing_sample = TRUE; diff --git a/src/common/mipmap_cache.c b/src/common/mipmap_cache.c index 622bd9837b70..3ad6c79c102b 100644 --- a/src/common/mipmap_cache.c +++ b/src/common/mipmap_cache.c @@ -257,7 +257,7 @@ void *dt_mipmap_cache_alloc(dt_mipmap_buffer_t *buf, const dt_image_t *img) const int ht = img->height; const size_t bpp = dt_iop_buffer_dsc_to_bpp(&img->buf_dsc); - const size_t buffer_size = (size_t)wd * ht * bpp + sizeof(*dsc); + const size_t buffer_size = (size_t)wd * ht * bpp * img->buf_dsc.frames + sizeof(*dsc); // buf might have been alloc'ed before, // so only check size and re-alloc if necessary: @@ -337,7 +337,7 @@ void dt_mipmap_cache_allocate_dynamic(void *data, dt_cache_entry_t *entry) entry->data = dt_alloc_align(64, entry->data_size); - // fprintf(stderr, "[mipmap cache] alloc dynamic for key %u %p\n", key, *buf); + fprintf(stderr, "[mipmap cache] alloc dynamic for key %u %p %lu\n", entry->key, entry->data, entry->data_size); if(!(entry->data)) { fprintf(stderr, "[mipmap cache] memory allocation failed!\n"); @@ -1285,10 +1285,11 @@ static void _init_8(uint8_t *buf, uint32_t *width, uint32_t *height, float *isca *iscale = 1.0f; *color_space = dt_mipmap_cache_get_colorspace(); } + fprintf(stderr, "[mipmap init 8] export image %u finished (sizes %d %d => %d %d)!\n", imgid, wd, ht, + dat.head.width, dat.head.height); } - // fprintf(stderr, "[mipmap init 8] export image %u finished (sizes %d %d => %d %d)!\n", imgid, wd, ht, - // dat.head.width, dat.head.height); + // any errors? if(res) diff --git a/src/develop/format.c b/src/develop/format.c index d9f6c62d63f7..f7246bbb5077 100644 --- a/src/develop/format.c +++ b/src/develop/format.c @@ -60,6 +60,7 @@ void default_input_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_de void default_output_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc) { + dsc->frames = 1; dsc->channels = 4; dsc->datatype = TYPE_FLOAT; dsc->cst = self->output_colorspace(self, pipe, piece); diff --git a/src/develop/format.h b/src/develop/format.h index 8f0f35b61b84..3bf592ee0dd8 100644 --- a/src/develop/format.h +++ b/src/develop/format.h @@ -36,6 +36,8 @@ typedef struct dt_iop_buffer_dsc_t { /** how many channels the data has? 1 or 4 */ unsigned int channels; + /** how many frames in multiframe images */ + unsigned frames; /** what is the datatype? */ dt_iop_buffer_type_t datatype; /** Bayer demosaic pattern */ diff --git a/src/develop/pixelpipe_hb.c b/src/develop/pixelpipe_hb.c index 6c0f36572e01..9b4827f8b8ed 100644 --- a/src/develop/pixelpipe_hb.c +++ b/src/develop/pixelpipe_hb.c @@ -41,6 +41,9 @@ #include #include #include +#include + +#define DEBUG_PIXELPIPE typedef enum dt_pixelpipe_flow_t { @@ -106,6 +109,141 @@ static char *_pipe_type_to_str(int pipe_type) return r; } +#ifdef DEBUG_PIXELPIPE +static void save_debug_bitmap(dt_dev_pixelpipe_t *pipe, const char *name, void *out, const dt_iop_roi_t *roi_out) +{ + if(pipe->type != DT_DEV_PIXELPIPE_FULL) + { + return; + } + + int single_channel = pipe->dsc.channels == 1; + int floating = pipe->dsc.datatype == TYPE_FLOAT; + + + char ext[8]; + if(floating) + { + snprintf(ext,8,"pfm"); + } + else + { + if(single_channel) + { + snprintf(ext,8,"pgm"); + } + else + { + snprintf(ext,8,"ppm"); + } + } + + char filename[128]; + snprintf(filename, 128, "save_debug_bitmap_%s_%s.%s", name, _pipe_type_to_str(pipe->type), ext); + for(int i = 0; i < 128; ++i) + { + if(filename[i] == '\0') + { + break; + } + if((filename[i] == ' ') || (filename[i] == '/')) + { + filename[i] = '_'; + } + } + + FILE *file = g_fopen(filename, "w"); + if(file == NULL) + { + fprintf(stderr, "error opening debug file: %s, %s\n", filename, strerror(errno)); + return; + } + if(floating) + { + if(!single_channel) + { + fprintf(file, "PF\n"); + } + else + { + fprintf(file, "Pf\n"); + } + } + else + { + if(single_channel) + { + fprintf(file, "P2\n"); + } + else + { + fprintf(file, "P3\n"); + } + } + fprintf(file, "%i %i\n", roi_out->width, roi_out->height * pipe->dsc.frames); + if(floating) + { + fprintf(file, "-1.0\n"); + } + else + { + fprintf(file,"%i\n", 16384); + } + if(floating) + { + float *ptr = (float *)out; + if((pipe->dsc.channels == 1) || (pipe->dsc.channels == 3)) + { + for(size_t f = 0; f < pipe->dsc.frames; ++f) + { + float *tmp = ptr + (f * roi_out->width * roi_out->height); + for(size_t i = 0; i < roi_out->width; ++i) + { + for(size_t j = 0; j < roi_out->height; ++j) + { + const size_t pout = j + (roi_out->height * (i)); + fwrite(tmp + pout, sizeof(float) * pipe->dsc.channels, 1, file); + } + } + } + } + else + { + for(size_t i = 0; i < roi_out->height * roi_out->width * pipe->dsc.frames * pipe->dsc.channels; + i += pipe->dsc.channels) + { + for(size_t j = 0; j < 3; ++j) + { + fwrite(ptr + i + j, sizeof(float), 1, file); + } + } + } + } + else + { + if(single_channel) + { + uint16_t *ptr = (uint16_t *)out; + for(size_t f = 0; f < pipe->dsc.frames; ++f) + { + uint16_t *tmp = ptr + (f * roi_out->width * roi_out->height); + for(size_t i = 0; i < roi_out->width; ++i) + { + for(size_t j = 0; j < roi_out->height; ++j) + { + const size_t pout = j + (roi_out->height * (i)); + fprintf(file,"%hu ",tmp[pout]); + //fwrite(tmp + pout, sizeof(uint16_t), 1, file); + } + fprintf(file,"\n"); + } + } + } + } + fclose(file); +} +#endif + int dt_dev_pixelpipe_init_export(dt_dev_pixelpipe_t *pipe, int32_t width, int32_t height, int levels, gboolean store_masks) { @@ -1097,7 +1235,7 @@ static int dt_dev_pixelpipe_process_rec(dt_dev_pixelpipe_t *pipe, dt_develop_t * if(module) g_strlcpy(module_name, module->op, MIN(sizeof(module_name), sizeof(module->op))); get_output_format(module, pipe, piece, dev, *out_format); const size_t bpp = dt_iop_buffer_dsc_to_bpp(*out_format); - const size_t bufsize = (size_t)bpp * roi_out->width * roi_out->height; + const size_t bufsize = (size_t)bpp * roi_out->width * roi_out->height * pipe->image.buf_dsc.frames; // 1) if cached buffer is still available, return data if(dt_atomic_get_int(&pipe->shutdown)) @@ -1171,19 +1309,31 @@ static int dt_dev_pixelpipe_process_rec(dt_dev_pixelpipe_t *pipe, dt_develop_t * const int in_y = MAX(roi_in.y, 0); const int cp_width = MAX(0, MIN(roi_out->width, pipe->iwidth - in_x)); const int cp_height = MIN(roi_out->height, pipe->iheight - in_y); + const int frames = pipe->dsc.frames; + + //fprintf(stderr, "[pipe] %s, input: %p\n", _pipe_type_to_str(pipe->type), pipe->input); + //fprintf(stderr, "[pipe] %s, output: %p\n", _pipe_type_to_str(pipe->type), *output); if (cp_width > 0) { #ifdef _OPENMP #pragma omp parallel for default(none) \ - dt_omp_firstprivate(bpp, cp_height, cp_width, in_x, in_y) \ + dt_omp_firstprivate(bpp, cp_height, cp_width, in_x, in_y, frames) \ shared(pipe, roi_out, roi_in, output) \ schedule(static) #endif - for(int j = 0; j < cp_height; j++) - memcpy(((char *)*output) + (size_t)bpp * j * roi_out->width, - ((char *)pipe->input) + (size_t)bpp * (in_x + (in_y + j) * pipe->iwidth), - (size_t)bpp * cp_width); + for(int f = 0; f < frames; ++f) + { + char *ptr = ((char *)pipe->input) + (f * bpp * pipe->iheight * pipe->iwidth); + char *outptr = ((char *)*output) + (f * bpp * roi_out->height * roi_out->width); + for(int j = 0; j < cp_height; j++) + memcpy(outptr + (size_t)bpp * j * roi_out->width, + ptr + (size_t)bpp * (in_x + (in_y + j) * pipe->iwidth), (size_t)bpp * cp_width); + } + +#ifdef DEBUG_PIXELPIPE + //save_debug_bitmap(pipe,"load",*output,roi_out); +#endif } } else @@ -1230,6 +1380,8 @@ static int dt_dev_pixelpipe_process_rec(dt_dev_pixelpipe_t *pipe, dt_develop_t * g_list_previous(modules), g_list_previous(pieces), pos - 1)) return 1; + //fprintf(stderr,"[pipe] %s, module: %s, input: %p\n",_pipe_type_to_str(pipe->type), module->op ,input); + const size_t in_bpp = dt_iop_buffer_dsc_to_bpp(input_format); piece->dsc_out = piece->dsc_in = *input_format; @@ -1259,6 +1411,8 @@ static int dt_dev_pixelpipe_process_rec(dt_dev_pixelpipe_t *pipe, dt_develop_t * // if(module) printf("reserving new buf in cache for module %s %s: %ld buf %p\n", module->op, pipe == // dev->preview_pipe ? "[preview]" : "", hash, *output); + //fprintf(stderr,"[pipe] %s, module: %s, output: %p\n",_pipe_type_to_str(pipe->type), module->op ,*output); + if(dt_atomic_get_int(&pipe->shutdown)) { return 1; @@ -1910,6 +2064,7 @@ static int dt_dev_pixelpipe_process_rec(dt_dev_pixelpipe_t *pipe, dt_develop_t * return 1; } #else // HAVE_OPENCL + if (pixelpipe_process_on_CPU(pipe, dev, input, input_format, &roi_in, output, out_format, roi_out, module, piece, &tiling, &pixelpipe_flow)) return 1; @@ -2031,6 +2186,11 @@ static int dt_dev_pixelpipe_process_rec(dt_dev_pixelpipe_t *pipe, dt_develop_t * } } +#ifdef DEBUG_PIXELPIPE + save_debug_bitmap(pipe,dt_history_item_get_name(module),*output,roi_out); +#endif + + // 4) colorpicker and scopes: if(dt_atomic_get_int(&pipe->shutdown)) { diff --git a/src/external/rawspeed b/src/external/rawspeed index 154081cbcf9a..a7cf21a5ef48 160000 --- a/src/external/rawspeed +++ b/src/external/rawspeed @@ -1 +1 @@ -Subproject commit 154081cbcf9a9141aa3508bbbe88fe8a810fcb6e +Subproject commit a7cf21a5ef487f47f11e0228af645b876e2d560a diff --git a/src/iop/demosaic.c b/src/iop/demosaic.c index 6613edc049c8..78fce52d7934 100644 --- a/src/iop/demosaic.c +++ b/src/iop/demosaic.c @@ -124,6 +124,7 @@ typedef struct dt_iop_demosaic_params_t dt_iop_demosaic_method_t demosaicing_method; // $DEFAULT: DT_IOP_DEMOSAIC_RCD $DESCRIPTION: "demosaicing method" dt_iop_demosaic_lmmse_t lmmse_refine; // $DEFAULT: LMMSE_REFINE_1 $DESCRIPTION: "lmmse refine" float dual_thrs; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.20 $DESCRIPTION: "dual threshold" + gboolean pixelshift_enable; // $DEFAULT: 0 $DESCRIPTION: "enable pixelshift" } dt_iop_demosaic_params_t; typedef struct dt_iop_demosaic_gui_data_t @@ -135,6 +136,10 @@ typedef struct dt_iop_demosaic_gui_data_t GtkWidget *demosaic_method_xtrans; GtkWidget *dual_thrs; GtkWidget *lmmse_refine; + GtkWidget *pixelshift_enable; + //GtkWidget *pixelshift_select_frame; + //GtkWidget *pixelshift_motion_correction; + //GtkWidget *pixelshift_show_motion_mask; gboolean visual_mask; } dt_iop_demosaic_gui_data_t; @@ -204,6 +209,7 @@ typedef struct dt_iop_demosaic_data_t float median_thrs; double CAM_to_RGB[3][4]; float dual_thrs; + gboolean pixelshift_enable; } dt_iop_demosaic_data_t; // Implemented on amaze_demosaic_RT.cc @@ -248,7 +254,18 @@ int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_p int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version) { - typedef struct dt_iop_demosaic_params_t dt_iop_demosaic_params_v4_t; + typedef struct dt_iop_demosaic_params_t dt_iop_demosaic_params_v5_t; + + typedef struct dt_iop_demosaic_params_v4_t + { + dt_iop_demosaic_greeneq_t green_eq; + float median_thrs; + dt_iop_demosaic_smooth_t color_smoothing; + dt_iop_demosaic_method_t demosaicing_method; + dt_iop_demosaic_lmmse_t lmmse_refine; + float dual_thrs; + } dt_iop_demosaic_params_v4_t; + typedef struct dt_iop_demosaic_params_v3_t { dt_iop_demosaic_greeneq_t green_eq; @@ -258,6 +275,15 @@ int legacy_params(dt_iop_module_t *self, const void *const old_params, const int dt_iop_demosaic_lmmse_t lmmse_refine; } dt_iop_demosaic_params_v3_t; + if(old_version == 4 && new_version == 5) + { + dt_iop_demosaic_params_v4_t *o = (dt_iop_demosaic_params_v4_t *)old_params; + dt_iop_demosaic_params_v5_t *n = (dt_iop_demosaic_params_v5_t *)new_params; + memcpy(n, o, sizeof *o); + n->pixelshift_enable = 0; + return 0; + } + if(old_version == 3 && new_version == 4) { dt_iop_demosaic_params_v3_t *o = (dt_iop_demosaic_params_v3_t *)old_params; @@ -2827,9 +2853,15 @@ void modify_roi_in(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t * const gboolean passthrough = (method == DT_IOP_DEMOSAIC_PASSTHROUGH_MONOCHROME) || (method == DT_IOP_DEMOSAIC_PASSTHR_MONOX); - // set position to closest sensor pattern snap - if(!passthrough) + if(data->pixelshift_enable) + { + //pixelshift requires 1px margin + roi_in->width = MIN(roi_in->width + 1, piece->pipe->image.width); + roi_in->height = MIN(roi_in->height + 1, piece->pipe->image.height); + } + else if(!passthrough) { + // set position to closest sensor pattern snap const int aligner = (piece->pipe->dsc.filters != 9u) ? BAYER_SNAPPER : XTRANS_SNAPPER; const int dx = roi_in->x % aligner; const int dy = roi_in->y % aligner; @@ -2936,6 +2968,68 @@ static int demosaic_qual_flags(const dt_dev_pixelpipe_iop_t *const piece, #include "dual_demosaic.c" +void process_pixelshift(dt_dev_pixelpipe_iop_t *piece, const float *const in, float *const out, const dt_iop_roi_t *const roi_in, + const dt_iop_roi_t *const roi_out) +{ + assert(roi_in->width >= roi_out->width); + assert(roi_in->height >= roi_out->height); + float const * frames_in[4]; + for(int f = 0; f < 4; ++f) + { + frames_in[f] = in + (f * roi_in->width * roi_in->height); + //fprintf(stderr, "frame %i: %p\n", f, frames_in[f]); + } + + if(piece->dsc_out.channels != 4) + { + fprintf(stderr, "unsuported number of output channels, got: %i\n", piece->dsc_out.channels); + } + + fprintf(stderr, "pixelshift enabled, pipe type: %s\n", dt_pixelpipe_name(piece->pipe->type)); + fprintf(stderr, "roi in %d %d %d %d\n", roi_in->x, roi_in->y, roi_in->width, roi_in->height); + fprintf(stderr, "roi out %d %d %d %d\n", roi_out->x, roi_out->y, roi_out->width, roi_out->height); + fprintf(stderr, "i: %p\n",in); + fprintf(stderr, "o: %p\n",out); + + /*__asan_describe_address((void*)in); + fflush(stdout); + fflush(stderr);*/ + + //const size_t ox = roi_in->x; + //const size_t oy = roi_in->y; + + const size_t col_offset = 1; + const size_t row_offset = roi_out->width; + + for(size_t j = 0; j < roi_out->height-1; j++) + { + for(size_t i = 0; i < roi_out->width-1; i++) + { + size_t pout = (size_t)4 * (((size_t)roi_out->width * j) + i); + size_t pin = (roi_in->width * j) + i; + + { + const uint32_t ch[4] = { FC(j + 0 + roi_out->y, i + 0 + roi_out->x, piece->pipe->dsc.filters), + FC(j + 1 + roi_out->y, i + 0 + roi_out->x, piece->pipe->dsc.filters), + FC(j + 1 + roi_out->y, i + 1 + roi_out->x, piece->pipe->dsc.filters), + FC(j + 0 + roi_out->y, i + 1 + roi_out->x, piece->pipe->dsc.filters) }; + for(int k = 0; k < 4; ++k) + { + out[pout + k] = 0.0f; + } + + out[pout + ch[0]] += frames_in[0][pin]; + out[pout + ch[1]] += frames_in[1][pin + row_offset]; + out[pout + ch[2]] += frames_in[2][pin + col_offset + row_offset]; + out[pout + ch[3]] += frames_in[3][pin + col_offset]; + + out[pout + 1] /= 2.0f; + } + } + } +} + + void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const i, void *const o, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out) { @@ -2945,6 +3039,9 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const dt_dev_clear_rawdetail_mask(piece->pipe); + fprintf(stderr, "demosaic, %s\n", dt_pixelpipe_name(piece->pipe->type)); + fprintf(stderr, "roi in %d %d %d %d\n", roi_in->x, roi_in->y, roi_in->width, roi_in->height); + fprintf(stderr, "roi out %d %d %d %d\n", roi_out->x, roi_out->y, roi_out->width, roi_out->height); dt_iop_roi_t roi = *roi_in; dt_iop_roi_t roo = *roi_out; roo.x = roo.y = 0; @@ -3019,6 +3116,10 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const else vng_interpolate(tmp, pixels, &roo, &roi, piece->pipe->dsc.filters, xtrans, qual_flags & DEMOSAIC_ONLY_VNG_LINEAR); } + else if(data->pixelshift_enable) + { + process_pixelshift(piece, pixels, tmp, &roi, &roo); + } else { float *in = (float *)pixels; @@ -3132,7 +3233,7 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const color_smoothing(o, roi_out, data->color_smoothing); } -#ifdef HAVE_OPENCL +#if 0 //#ifdef HAVE_OPENCL // color smoothing step by multiple passes of median filtering static int color_smoothing_cl(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out, const dt_iop_roi_t *const roi_out, const int passes) @@ -5559,6 +5660,17 @@ void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *params, dt_dev d->median_thrs = p->median_thrs; d->dual_thrs = p->dual_thrs; d->lmmse_refine = p->lmmse_refine; + if(pipe->type & (DT_DEV_PIXELPIPE_FULL | DT_DEV_PIXELPIPE_EXPORT)) + { + d->pixelshift_enable = p->pixelshift_enable; + fprintf(stderr, "demosaic ,commit_params, %s, pixelshift in: %i out: %i\n", dt_pixelpipe_name(pipe->type), p->pixelshift_enable, d->pixelshift_enable); + } + else + { + d->pixelshift_enable = FALSE; + } + + dt_iop_demosaic_method_t use_method = p->demosaicing_method; const gboolean xmethod = use_method & DEMOSAIC_XTRANS; const gboolean bayer = (self->dev->image_storage.buf_dsc.filters != 9u); @@ -5636,6 +5748,12 @@ void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *params, dt_dev piece->process_cl_ready = 0; } + ///TODO make this better + if(d->pixelshift_enable) + { + piece->process_cl_ready = 0; + } + // green-equilibrate over full image excludes tiling // The details mask is written inside process, this does not allow tiling. if((d->green_eq == DT_IOP_GREEN_EQ_FULL @@ -5711,16 +5829,33 @@ void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous) (use_method == DT_IOP_DEMOSAIC_PASSTHROUGH_COLOR) || (use_method == DT_IOP_DEMOSAIC_PASSTHR_MONOX) || (use_method == DT_IOP_DEMOSAIC_PASSTHR_COLORX)); + ///TODO doesn't take into account previous modules? + const gboolean ispixelshift = bayer && (self->dev->image_storage.buf_dsc.frames == 4); + + gtk_widget_set_visible(g->pixelshift_enable, bayer && ispixelshift); + + if(!bayer || !ispixelshift) + { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->pixelshift_enable), FALSE); + p->pixelshift_enable = FALSE; + } + else + { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->pixelshift_enable), + p->pixelshift_enable && bayer && ispixelshift); + } - gtk_widget_set_visible(g->demosaic_method_bayer, bayer); + //gtk_widget_set_visible(g->pixelshift_select_frame, bayer && ispixelshift); + + gtk_widget_set_visible(g->demosaic_method_bayer, bayer && !p->pixelshift_enable); gtk_widget_set_visible(g->demosaic_method_xtrans, !bayer); if(bayer) dt_bauhaus_combobox_set_from_value(g->demosaic_method_bayer, p->demosaicing_method); else dt_bauhaus_combobox_set_from_value(g->demosaic_method_xtrans, p->demosaicing_method); - gtk_widget_set_visible(g->median_thrs, bayer && isppg); - gtk_widget_set_visible(g->greeneq, !passing); + gtk_widget_set_visible(g->median_thrs, bayer && isppg && !p->pixelshift_enable); + gtk_widget_set_visible(g->greeneq, !passing && !p->pixelshift_enable); gtk_widget_set_visible(g->color_smoothing, !passing && !isdual); gtk_widget_set_visible(g->dual_thrs, isdual); gtk_widget_set_visible(g->lmmse_refine, islmmse); @@ -5781,19 +5916,21 @@ void gui_init(struct dt_iop_module_t *self) GtkWidget *box_raw = self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_BAUHAUS_SPACE); + g->pixelshift_enable = dt_bauhaus_toggle_from_params(self, "pixelshift_enable"); + gtk_widget_set_tooltip_text(g->pixelshift_enable, _("Enable Pentax PixelShift.\nThis feature is beta stage might not work correctly.\nThis also doesn't support motion correction yet.")); + g->demosaic_method_bayer = dt_bauhaus_combobox_from_params(self, "demosaicing_method"); for(int i=0;i<7;i++) dt_bauhaus_combobox_remove_at(g->demosaic_method_bayer, 9); gtk_widget_set_tooltip_text(g->demosaic_method_bayer, _("Bayer sensor demosaicing method, PPG and RCD are fast, AMaZE and LMMSE are slow.\nLMMSE is suited best for high ISO images.\ndual demosaicers double processing time.")); g->demosaic_method_xtrans = dt_bauhaus_combobox_from_params(self, "demosaicing_method"); - for(int i=0;i<9;i++) dt_bauhaus_combobox_remove_at(g->demosaic_method_xtrans, 0); + //for(int i=0;i<10;i++) dt_bauhaus_combobox_remove_at(g->demosaic_method_xtrans, 0); gtk_widget_set_tooltip_text(g->demosaic_method_xtrans, _("X-Trans sensor demosaicing method, Markesteijn 3-pass and frequency domain chroma are slow.\ndual demosaicers double processing time.")); g->median_thrs = dt_bauhaus_slider_from_params(self, "median_thrs"); dt_bauhaus_slider_set_digits(g->median_thrs, 3); gtk_widget_set_tooltip_text(g->median_thrs, _("threshold for edge-aware median.\nset to 0.0 to switch off\n" "set to 1.0 to ignore edges")); - g->dual_thrs = dt_bauhaus_slider_from_params(self, "dual_thrs"); dt_bauhaus_slider_set_digits(g->dual_thrs, 2); gtk_widget_set_tooltip_text(g->dual_thrs, _("contrast threshold for dual demosaic.\nset to 0.0 for high frequency content\n" @@ -5812,6 +5949,13 @@ void gui_init(struct dt_iop_module_t *self) g->greeneq = dt_bauhaus_combobox_from_params(self, "green_eq"); gtk_widget_set_tooltip_text(g->greeneq, _("green channels matching method")); + /*g->pixelshift_select_frame = dt_bauhaus_combobox_new(self); + dt_bauhaus_widget_set_label(g->pixelshift_select_frame,NULL,"input frames"); + gtk_widget_set_tooltip_text(g->pixelshift_select_frame,"input frames"); + dt_bauhaus_combobox_add(g->pixelshift_select_frame,"all"); + dt_bauhaus_combobox_add(g->pixelshift_select_frame,"test"); + gtk_box_pack_start(GTK_BOX(self->widget), g->pixelshift_select_frame, FALSE, FALSE, 0);*/ + // start building top level widget self->widget = gtk_stack_new(); gtk_stack_set_homogeneous(GTK_STACK(self->widget), FALSE); diff --git a/src/iop/rawprepare.c b/src/iop/rawprepare.c index eaec280e02dc..9c71150910d8 100644 --- a/src/iop/rawprepare.c +++ b/src/iop/rawprepare.c @@ -231,7 +231,15 @@ void output_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixel dt_iop_buffer_dsc_t *dsc) { default_output_format(self, pipe, piece, dsc); - + // multiframe not needed for subsampled images + if(pipe->type & (DT_DEV_PIXELPIPE_FULL | DT_DEV_PIXELPIPE_EXPORT)) + { + dsc->frames = pipe->image.buf_dsc.frames; + } + else + { + dsc->frames = 1; + } dt_iop_rawprepare_data_t *d = (dt_iop_rawprepare_data_t *)piece->data; dsc->rawprepare.raw_black_level = d->rawprepare.raw_black_level; @@ -256,6 +264,88 @@ static int BL(const dt_iop_roi_t *const roi_out, const dt_iop_rawprepare_data_t return ((((row + roi_out->y + d->y) & 1) << 1) + ((col + roi_out->x + d->x) & 1)); } +static void convert_uint_float(const uint16_t *const in, float *const out, const dt_iop_roi_t *const roi_in, + const dt_iop_roi_t *const roi_out, int csx, int csy, + const dt_iop_rawprepare_data_t *const d) +{ + /*fprintf(stderr,"in: %p - %p, size %i\n",in, in + (roi_in->height * roi_in->width), (roi_in->height * roi_in->width)); + fprintf(stderr,"out: %p - %p, size %i\n",out, out + (roi_out->height * roi_out->width),(roi_out->height * roi_out->width));*/ +#ifdef _OPENMP +#pragma omp parallel for SIMD() default(none) \ + dt_omp_firstprivate(csx, csy, d, in, out, roi_in, roi_out) \ + schedule(static) collapse(2) +#endif + for(int j = 0; j < roi_out->height; j++) + { + for(int i = 0; i < roi_out->width; i++) + { + const size_t pin = (size_t)(roi_in->width * (j + csy) + csx) + i; + const size_t pout = (size_t)j * roi_out->width + i; + + const int id = BL(roi_out, d, j, i); + out[pout] = (in[pin] - d->sub[id]) / d->div[id]; + } + } + + /*for(int j = roi_out->height-100; j < roi_out->height; j++) + { + for(int i = 0; i < roi_out->width; i++) + { + //const size_t pin = (size_t)(roi_in->width * (j + csy) + csx) + i; + const size_t pout = (size_t)j * roi_out->width + i; + + + out[pout] = 0.0f; + } + }*/ +} + +static void convert_float_float(const float *const in, float *const out, const dt_iop_roi_t *const roi_in, + const dt_iop_roi_t *const roi_out, int csx, int csy, + const dt_iop_rawprepare_data_t *const d) +{ +#ifdef _OPENMP +#pragma omp parallel for SIMD() default(none) \ + dt_omp_firstprivate(csx, csy, d, in, out, roi_in, roi_out) \ + schedule(static) collapse(2) +#endif + for(int j = 0; j < roi_out->height; j++) + { + for(int i = 0; i < roi_out->width; i++) + { + const size_t pin = (size_t)(roi_in->width * (j + csy) + csx) + i; + const size_t pout = (size_t)j * roi_out->width + i; + + const int id = BL(roi_out, d, j, i); + out[pout] = (in[pin] - d->sub[id]) / d->div[id]; + } + } +} + +static void convert_float_downsampled(const float *const in, float *const out, const dt_iop_roi_t *const roi_in, + const dt_iop_roi_t *const roi_out, int csx, int csy, + const dt_iop_rawprepare_data_t *const d, int ch, float sub, float div) +{ +#ifdef _OPENMP +#pragma omp parallel for SIMD() default(none) \ + dt_omp_firstprivate(ch, csx, csy, div, in, out, roi_in, roi_out, sub) \ + schedule(static) collapse(3) +#endif + for(int j = 0; j < roi_out->height; j++) + { + for(int i = 0; i < roi_out->width; i++) + { + for(int c = 0; c < ch; c++) + { + const size_t pin = (size_t)ch * (roi_in->width * (j + csy) + csx + i) + c; + const size_t pout = (size_t)ch * (j * roi_out->width + i) + c; + + out[pout] = (in[pin] - sub) / div; + } + } + } +} + /* Some comments about the cpu code path; tests with gcc 10.x show a clear performance gain for the compile generated code vs SSE specific code. This depends slightly on the cpu but it's 1.2 to 3-fold better for all tested cases. @@ -265,10 +355,16 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const { const dt_iop_rawprepare_data_t *const d = (dt_iop_rawprepare_data_t *)piece->data; - // fprintf(stderr, "roi in %d %d %d %d\n", roi_in->x, roi_in->y, roi_in->width, roi_in->height); - // fprintf(stderr, "roi out %d %d %d %d\n", roi_out->x, roi_out->y, roi_out->width, roi_out->height); + fprintf(stderr,"rawprepare %s\n", dt_pixelpipe_name(piece->pipe->type)); + fprintf(stderr, "roi in %d %d %d %d\n", roi_in->x, roi_in->y, roi_in->width, roi_in->height); + fprintf(stderr, "roi out %d %d %d %d\n", roi_out->x, roi_out->y, roi_out->width, roi_out->height); + /*fprintf(stderr,"frame size in: %d\n", roi_in->width * roi_in->height); + fprintf(stderr,"frame size iout: %d\n", roi_out->width * roi_out->height);*/ + //fprintf(stderr,"i: %p\n",ivoid); + //fprintf(stderr,"o: %p\n",ovoid); - const int csx = compute_proper_crop(piece, roi_in, d->x), csy = compute_proper_crop(piece, roi_in, d->y); + const int csx = compute_proper_crop(piece, roi_in, d->x); + const int csy = compute_proper_crop(piece, roi_in, d->y); if(piece->pipe->dsc.filters && piece->dsc_in.channels == 1 && piece->dsc_in.datatype == TYPE_UINT16) @@ -277,22 +373,12 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const const uint16_t *const in = (const uint16_t *const)ivoid; float *const out = (float *const)ovoid; -#ifdef _OPENMP -#pragma omp parallel for SIMD() default(none) \ - dt_omp_firstprivate(csx, csy, d, in, out, roi_in, roi_out) \ - schedule(static) \ - collapse(2) -#endif - for(int j = 0; j < roi_out->height; j++) + for(size_t f = 0; f < piece->dsc_out.frames; ++f) { - for(int i = 0; i < roi_out->width; i++) - { - const size_t pin = (size_t)(roi_in->width * (j + csy) + csx) + i; - const size_t pout = (size_t)j * roi_out->width + i; - - const int id = BL(roi_out, d, j, i); - out[pout] = (in[pin] - d->sub[id]) / d->div[id]; - } + const uint16_t *const frame_in = in + (f * roi_in->width * roi_in->height); + float *const frame_out = out + (f * roi_out->width * roi_out->height); + convert_uint_float(frame_in, frame_out, roi_in, roi_out, csx, csy, d); + //fprintf(stderr, "frame %lu: %p\n", f, frame_in); } piece->pipe->dsc.filters = dt_rawspeed_crop_dcraw_filters(self->dev->image_storage.buf_dsc.filters, csx, csy); @@ -301,26 +387,16 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const else if(piece->pipe->dsc.filters && piece->dsc_in.channels == 1 && piece->dsc_in.datatype == TYPE_FLOAT) { // raw mosaic, fp, unnormalized + ///TODO test const float *const in = (const float *const)ivoid; float *const out = (float *const)ovoid; -#ifdef _OPENMP -#pragma omp parallel for SIMD() default(none) \ - dt_omp_firstprivate(csx, csy, d, in, out, roi_in, roi_out) \ - schedule(static) \ - collapse(2) -#endif - for(int j = 0; j < roi_out->height; j++) + for(size_t f = 0; f < piece->dsc_in.frames; ++f) { - for(int i = 0; i < roi_out->width; i++) - { - const size_t pin = (size_t)(roi_in->width * (j + csy) + csx) + i; - const size_t pout = (size_t)j * roi_out->width + i; - - const int id = BL(roi_out, d, j, i); - out[pout] = (in[pin] - d->sub[id]) / d->div[id]; - } + const float *const frame_in = in + (f * roi_in->width * roi_in->height); + float *const frame_out = out + (f * roi_out->width * roi_out->height); + convert_float_float(frame_in, frame_out, roi_in, roi_out, csx, csy, d); } piece->pipe->dsc.filters = dt_rawspeed_crop_dcraw_filters(self->dev->image_storage.buf_dsc.filters, csx, csy); @@ -328,32 +404,22 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const } else { // pre-downsampled buffer that needs black/white scaling + ///TODO test const float *const in = (const float *const)ivoid; float *const out = (float *const)ovoid; - const float sub = d->sub[0], div = d->div[0]; - + const float sub = d->sub[0]; + const float div = d->div[0]; const int ch = piece->colors; -#ifdef _OPENMP -#pragma omp parallel for SIMD() default(none) \ - dt_omp_firstprivate(ch, csx, csy, div, in, out, roi_in, roi_out, sub) \ - schedule(static) collapse(3) -#endif - for(int j = 0; j < roi_out->height; j++) + for(size_t f = 0; f < piece->dsc_in.frames; ++f) { - for(int i = 0; i < roi_out->width; i++) - { - for(int c = 0; c < ch; c++) - { - const size_t pin = (size_t)ch * (roi_in->width * (j + csy) + csx + i) + c; - const size_t pout = (size_t)ch * (j * roi_out->width + i) + c; - - out[pout] = (in[pin] - sub) / div; - } - } + const float *const frame_in = in + (f * roi_in->width * roi_in->height); + float *const frame_out = out + (f * roi_out->width * roi_out->height); + convert_float_downsampled(frame_in, frame_out, roi_in, roi_out, csx, csy, d,ch,sub,div); } + } dt_dev_write_rawdetail_mask(piece, (float *const)ovoid, roi_in, DT_DEV_DETAIL_MASK_RAWPREPARE); @@ -361,6 +427,7 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const for(int k = 0; k < 4; k++) piece->pipe->dsc.processed_maximum[k] = 1.0f; } +#if 0 ///TODO modify to handle multiframe #ifdef HAVE_OPENCL int process_cl(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out) @@ -436,6 +503,7 @@ int process_cl(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, cl_mem dev_ return FALSE; } #endif +#endif static int image_is_normalized(const dt_image_t *const image) { diff --git a/src/iop/temperature.c b/src/iop/temperature.c index 6bf008f732c1..a37b63d132c3 100644 --- a/src/iop/temperature.c +++ b/src/iop/temperature.c @@ -221,6 +221,14 @@ int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_p return IOP_CS_RAW; } +void output_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, + dt_iop_buffer_dsc_t *dsc) +{ + default_output_format(self, pipe, piece, dsc); + ///TODO is this correct? + dsc->frames = pipe->dsc.frames; +} + /* * Spectral power distribution functions * https://en.wikipedia.org/wiki/Spectral_power_distribution @@ -466,6 +474,8 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const float *const out = (float *const)ovoid; const float *const d_coeffs = d->coeffs; + fprintf(stderr, "temperature, %s\n", dt_pixelpipe_name(piece->pipe->type)); + if(filters == 9u) { // xtrans float mosaiced #ifdef _OPENMP @@ -503,39 +513,55 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const } else if(filters) { // bayer float mosaiced - const int width = roi_out->width; + for(int f=0;fdsc_in.frames;++f) + { + float const *const frame_in = in + (f * roi_in->width * roi_in->height); + float *const frame_out = out + (f * roi_out->width * roi_out->height); + //fprintf(stderr,"frame in %p\n", frame_in); + //fprintf(stderr,"frame out %p\n", frame_out); + const int width = roi_out->width; #ifdef _OPENMP #pragma omp parallel for default(none) \ - dt_omp_firstprivate(d_coeffs, filters, in, out, roi_out, width) \ - schedule(static) + dt_omp_firstprivate(d_coeffs, filters, frame_in, frame_out, roi_out, width) \ + schedule(static) #endif - for(int j = 0; j < roi_out->height; j++) - { - int i = 0; - const int alignment = ((4 - (j * width & (4 - 1))) & (4 - 1)); - const int offset_j = j + roi_out->y; - - // process the unaligned sensels at the start of the row (when width is not a multiple of 4) - for( ; i < alignment; i++) - { - const size_t p = (size_t)j * width + i; - out[p] = in[p] * d_coeffs[FC(offset_j, i + roi_out->x, filters)]; - } - const dt_aligned_pixel_t coeffs = { d_coeffs[FC(offset_j, i + roi_out->x, filters)], - d_coeffs[FC(offset_j, i + roi_out->x + 1,filters)], - d_coeffs[FC(offset_j, i + roi_out->x + 2, filters)], - d_coeffs[FC(offset_j, i + roi_out->x + 3, filters)] }; - // process sensels four at a time - for(; i < (width & ~3); i += 4) - { - const size_t p = (size_t)j * width + i; - scaled_copy_4wide(out + p,in + p, coeffs); - } - // process the leftover sensels - for(i = width & ~3; i < width; i++) + for(int j = 0; j < roi_out->height; j++) { - const size_t p = (size_t)j * width + i; - out[p] = in[p] * d_coeffs[FC(j + roi_out->y, i + roi_out->x, filters)]; +#if 0 +// this optimization does not work for multiframe + int i = 0; + const int alignment = ((4 - (j * width & (4 - 1))) & (4 - 1)); + const int offset_j = j + roi_out->y; + + // process the unaligned sensels at the start of the row (when width is not a multiple of 4) + for( ; i < alignment; i++) + { + const size_t p = (size_t)j * width + i; + frame_out[p] = frame_in[p] * d_coeffs[FC(offset_j, i + roi_out->x, filters)]; + } + const dt_aligned_pixel_t coeffs = { d_coeffs[FC(offset_j, i + roi_out->x, filters)], + d_coeffs[FC(offset_j, i + roi_out->x + 1,filters)], + d_coeffs[FC(offset_j, i + roi_out->x + 2, filters)], + d_coeffs[FC(offset_j, i + roi_out->x + 3, filters)] }; + // process sensels four at a time + for(; i < (width & ~3); i += 4) + { + const size_t p = (size_t)j * width + i; + scaled_copy_4wide(frame_out + p,frame_in + p, coeffs); + } + // process the leftover sensels + for(i = width & ~3; i < width; i++) + { + const size_t p = (size_t)j * width + i; + frame_out[p] = frame_in[p] * d_coeffs[FC(j + roi_out->y, i + roi_out->x, filters)]; + } +#endif + for(int i = 0; i < roi_out->width; i++) + { + const size_t p = (size_t)j * width + i; + frame_out[p] = frame_in[p] * d_coeffs[FC(j + roi_out->y, i + roi_out->x, filters)]; + } + } } } @@ -571,6 +597,7 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const } } +#if 0 #if defined(__SSE__) void process_sse2(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out) @@ -621,7 +648,9 @@ void process_sse2(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, c } } #endif +#endif +#if 0 #ifdef HAVE_OPENCL int process_cl(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out) @@ -694,6 +723,7 @@ int process_cl(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, cl_m return FALSE; } #endif +#endif void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)