예제 #1
0
/** process, all real work is done here. */
void process (struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, void *i, void *o, const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out)
{
  // this is called for preview and full pipe separately, each with its own pixelpipe piece.
  assert(dt_iop_module_colorspace(self) == iop_cs_Lab);
  // get our data struct:
  dt_iop_colorcontrast_params_t *d = (dt_iop_colorcontrast_params_t *)piece->data;
  // how many colors in our buffer?
  const int ch = piece->colors;
  // iterate over all output pixels (same coordinates as input)
#ifdef _OPENMP
  // optional: parallelize it!
  #pragma omp parallel for default(none) schedule(static) shared(i,o,roi_in,roi_out,d)
#endif
  for(int j=0; j<roi_out->height; j++)
  {

    float *in  = ((float *)i) + ch*roi_in->width *j;
    float *out = ((float *)o) + ch*roi_out->width*j;

    const __m128 scale = _mm_set_ps(0.0f,d->b_steepness,d->a_steepness,1.0f);
    const __m128 offset = _mm_set_ps(0.0f,d->b_offset,d->a_offset,0.0f);
    const __m128 min = _mm_set_ps(0.0f,-128.0f,-128.0f, -INFINITY);
    const __m128 max = _mm_set_ps(0.0f, 128.0f, 128.0f,  INFINITY);


    for(int i=0; i<roi_out->width; i++)
    {
      _mm_stream_ps(out,_mm_min_ps(max,_mm_max_ps(min,_mm_add_ps(offset,_mm_mul_ps(scale,_mm_load_ps(in))))));
      in+=ch;
      out+=ch;
    }
  }
  _mm_sfence();
}
예제 #2
0
void dt_iop_gui_init_blending(GtkWidget *iopw, dt_iop_module_t *module)
{
  /* create and add blend mode if module supports it */
  if (module->flags()&IOP_FLAGS_SUPPORTS_BLENDING)
  {
    module->blend_data = g_malloc(sizeof(dt_iop_gui_blend_data_t));
    memset(module->blend_data, 0, sizeof(dt_iop_gui_blend_data_t));
    dt_iop_gui_blend_data_t *bd = (dt_iop_gui_blend_data_t*)module->blend_data;

    dt_iop_gui_blendop_modes_t modes[23]; /* number must fit exactly!!! */
    modes[0].mode  = DEVELOP_BLEND_DISABLED;
    modes[0].name  = _("off");
    modes[1].mode  = DEVELOP_BLEND_NORMAL;
    modes[1].name  = _("normal");
    modes[2].mode  = DEVELOP_BLEND_INVERSE;
    modes[2].name  = _("inverse");
    modes[3].mode  = DEVELOP_BLEND_LIGHTEN;
    modes[3].name  = _("lighten");
    modes[4].mode  = DEVELOP_BLEND_DARKEN;
    modes[4].name  = _("darken");
    modes[5].mode  = DEVELOP_BLEND_MULTIPLY;
    modes[5].name  = _("multiply");
    modes[6].mode  = DEVELOP_BLEND_AVERAGE;
    modes[6].name  = _("average");
    modes[7].mode  = DEVELOP_BLEND_ADD;
    modes[7].name  = _("addition");
    modes[8].mode  = DEVELOP_BLEND_SUBSTRACT;
    modes[8].name  = _("subtract");
    modes[9].mode  = DEVELOP_BLEND_DIFFERENCE;
    modes[9].name  = _("difference");
    modes[10].mode = DEVELOP_BLEND_SCREEN;
    modes[10].name = _("screen");
    modes[11].mode = DEVELOP_BLEND_OVERLAY;
    modes[11].name = _("overlay");
    modes[12].mode = DEVELOP_BLEND_SOFTLIGHT;
    modes[12].name = _("softlight");
    modes[13].mode = DEVELOP_BLEND_HARDLIGHT;
    modes[13].name = _("hardlight");
    modes[14].mode = DEVELOP_BLEND_VIVIDLIGHT;
    modes[14].name = _("vividlight");
    modes[15].mode = DEVELOP_BLEND_LINEARLIGHT;
    modes[15].name = _("linearlight");
    modes[16].mode = DEVELOP_BLEND_PINLIGHT;
    modes[16].name = _("pinlight");
    modes[17].mode = DEVELOP_BLEND_LIGHTNESS;
    modes[17].name = _("lightness");
    modes[18].mode = DEVELOP_BLEND_CHROMA;
    modes[18].name = _("chroma");
    modes[19].mode = DEVELOP_BLEND_HUE;
    modes[19].name = _("hue");
    modes[20].mode = DEVELOP_BLEND_COLOR;
    modes[20].name = _("color");
    modes[21].mode = DEVELOP_BLEND_COLORADJUST;
    modes[21].name = _("coloradjustment");
    modes[22].mode = DEVELOP_BLEND_UNBOUNDED;
    modes[22].name = _("unbounded");


    bd->number_modes = sizeof(modes) / sizeof(dt_iop_gui_blendop_modes_t);
    memcpy(bd->modes, modes, bd->number_modes * sizeof(dt_iop_gui_blendop_modes_t));
    bd->iopw = iopw;
    bd->module = module;
    bd->csp = dt_iop_module_colorspace(module);
    bd->blendif_support = (bd->csp == iop_cs_Lab || bd->csp == iop_cs_rgb);
    bd->blendif_box = NULL;

    bd->blend_modes_combo = dt_bauhaus_combobox_new(module);
    dt_bauhaus_widget_set_label(bd->blend_modes_combo, _("blend mode"));
    bd->opacity_slider = dt_bauhaus_slider_new_with_range(module, 0.0, 100.0, 1, 100.0, 0);
    dt_bauhaus_widget_set_label(bd->opacity_slider, _("opacity"));
    dt_bauhaus_slider_set_format(bd->opacity_slider, "%.0f%%");
    module->fusion_slider = bd->opacity_slider;


    for(int k = 0; k < bd->number_modes; k++)
      dt_bauhaus_combobox_add(bd->blend_modes_combo, bd->modes[k].name);

    dt_bauhaus_combobox_set(bd->blend_modes_combo, 0);

    gtk_object_set(GTK_OBJECT(bd->opacity_slider), "tooltip-text", _("set the opacity of the blending"), (char *)NULL);
    gtk_object_set(GTK_OBJECT(bd->blend_modes_combo), "tooltip-text", _("choose blending mode"), (char *)NULL);

    g_signal_connect (G_OBJECT (bd->opacity_slider), "value-changed",
                      G_CALLBACK (_blendop_opacity_callback), bd);
    g_signal_connect (G_OBJECT (bd->blend_modes_combo), "value-changed",
                      G_CALLBACK (_blendop_mode_callback), bd);

    gtk_box_pack_start(GTK_BOX(iopw), bd->blend_modes_combo, TRUE, TRUE,0);
    gtk_box_pack_start(GTK_BOX(iopw), bd->opacity_slider, TRUE, TRUE,0);

    if(bd->blendif_support)
    {
      dt_iop_gui_init_blendif(GTK_VBOX(iopw), module);
    }
    bd->blend_inited = 1;
    gtk_widget_queue_draw(GTK_WIDGET(iopw));
    dt_iop_gui_update_blending(module);
  }
}
예제 #3
0
// the basis of how the following algorithm works comes from rawtherapee (http://rawtherapee.com/)
// defringe -- thanks to Emil Martinec <*****@*****.**> for that
// quite some modifications were done though:
// 1. use a fibonacci lattice instead of full window, to speed things up
// 2. option for local averaging or static (RT used the global/region one)
// 3. additional condition to reduce sharp edged artifacts, by blurring pixels near pixels over threshold,
// this really helps improving the filter with thick fringes
// -----------------------------------------------------------------------------------------
// in the following you will also see some more "magic numbers",
// most are chosen arbitrarily and/or by experiment/trial+error ... I am sorry ;-)
// and having everything user-defineable would be just too much
// -----------------------------------------------------------------------------------------
void process(struct dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece, void *i, void *o,
             const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out)
{
  dt_iop_defringe_data_t *d = (dt_iop_defringe_data_t *)piece->data;
  assert(dt_iop_module_colorspace(module) == iop_cs_Lab);

  const int order = 1; // 0,1,2
  const float sigma = fmax(0.1f, fabs(d->radius)) * roi_in->scale / piece->iscale;
  const float Labmax[] = { 100.0f, 128.0f, 128.0f, 1.0f };
  const float Labmin[] = { 0.0f, -128.0f, -128.0f, 0.0f };
  const int ch = piece->colors;

  const int radius = ceil(2.0 * ceilf(sigma));

  // save the fibonacci lattices in them later
  int *xy_avg = NULL;
  int *xy_artifact = NULL;
  int *xy_small = NULL;

  if(roi_out->width < 2 * radius + 1 || roi_out->height < 2 * radius + 1) goto ERROR_EXIT;

  float avg_edge_chroma = 0.0;

  float *const in = (float *const)i;
  float *const out = (float *const)o;
  int width = roi_in->width;
  int height = roi_in->height;

  dt_gaussian_t *gauss = NULL;
  gauss = dt_gaussian_init(width, height, ch, Labmax, Labmin, sigma, order);
  if(!gauss)
  {
    fprintf(stderr, "Error allocating memory for gaussian blur in: defringe module\n");
    goto ERROR_EXIT;
  }
  dt_gaussian_blur(gauss, in, out);
  dt_gaussian_free(gauss);

  // Pre-Compute Fibonacci Lattices
  int *tmp;

  int samples_wish = radius * radius;
  int sampleidx_avg;
  // select samples by fibonacci number
  if(samples_wish > 89)
  {
    sampleidx_avg = 12; // 144 samples
  }
  else if(samples_wish > 55)
  {
    sampleidx_avg = 11; // 89 samples
  }
  else if(samples_wish > 34)
  {
    sampleidx_avg = 10; // ..you get the idea
  }
  else if(samples_wish > 21)
  {
    sampleidx_avg = 9;
  }
  else if(samples_wish > 13)
  {
    sampleidx_avg = 8;
  }
  else
  { // don't use less than 13 samples
    sampleidx_avg = 7;
  }
  const int sampleidx_small = sampleidx_avg - 1;

  const int small_radius = MAX(radius, 3);
  const int avg_radius = 24 + radius * 4;

  const int samples_small = fib[sampleidx_small];
  const int samples_avg = fib[sampleidx_avg];

  // precompute all required fibonacci lattices:
  if((xy_avg = malloc((size_t)2 * sizeof(int) * samples_avg)))
  {
    tmp = xy_avg;
    for(int u = 0; u < samples_avg; u++)
    {
      int dx, dy;
      fib_latt(&dx, &dy, avg_radius, u, sampleidx_avg);
      *tmp++ = dx;
      *tmp++ = dy;
    }
  }
  else
  {
    fprintf(stderr, "Error allocating memory for fibonacci lattice in: defringe module\n");
    goto ERROR_EXIT;
  }

  if((xy_small = malloc((size_t)2 * sizeof(int) * samples_small)))
  {
    tmp = xy_small;
    for(int u = 0; u < samples_small; u++)
    {
      int dx, dy;
      fib_latt(&dx, &dy, small_radius, u, sampleidx_small);
      *tmp++ = dx;
      *tmp++ = dy;
    }
  }
  else
  {
    fprintf(stderr, "Error allocating memory for fibonacci lattice in: defringe module\n");
    goto ERROR_EXIT;
  }

#ifdef _OPENMP
#pragma omp parallel for default(none) shared(width, height,                                                 \
                                              d) reduction(+ : avg_edge_chroma) schedule(static)
#endif
  for(int v = 0; v < height; v++)
  {
    for(int t = 0; t < width; t++)
    {
      // edge-detect on color channels
      // method: difference of original to gaussian blurred image:
      float a = in[(size_t)v * width * ch + t * ch + 1] - out[(size_t)v * width * ch + t * ch + 1];
      float b = in[(size_t)v * width * ch + t * ch + 2] - out[(size_t)v * width * ch + t * ch + 2];

      float edge = (a * a + b * b); // range up to 2*(256)^2 -> approx. 0 to 131072

      // save local edge chroma in out[.. +3] , this is later compared with threshold
      out[(size_t)v * width * ch + t * ch + 3] = edge;
      // the average chroma of the edge-layer in the roi
      if(MODE_GLOBAL_AVERAGE == d->op_mode) avg_edge_chroma += edge;
    }
  }

  float thresh;
  if(MODE_GLOBAL_AVERAGE == d->op_mode)
  {
    avg_edge_chroma = avg_edge_chroma / (width * height) + 10.0 * FLT_EPSILON;
    thresh = fmax(0.1f, 4.0 * d->thresh * avg_edge_chroma / MAGIC_THRESHOLD_COEFF);
  }
  else
  {
    // this fixed value will later be changed when doing local averaging, or kept as-is in "static" mode
    avg_edge_chroma = MAGIC_THRESHOLD_COEFF;
    thresh = fmax(0.1f, d->thresh);
  }

#ifdef _OPENMP
// dynamically/guided scheduled due to possible uneven edge-chroma distribution (thanks to rawtherapee code
// for this hint!)
#pragma omp parallel for default(none) shared(width, height, d, xy_small, xy_avg, xy_artifact)               \
    firstprivate(thresh, avg_edge_chroma) schedule(guided, 32)
#endif
  for(int v = 0; v < height; v++)
  {
    for(int t = 0; t < width; t++)
    {
      float local_thresh = thresh;
      // think of compiler setting "-funswitch-loops" to maybe improve these things:
      if(MODE_LOCAL_AVERAGE == d->op_mode && out[(size_t)v * width * ch + t * ch + 3] > thresh)
      {
        float local_avg = 0.0;
        // use some and not all values from the neigbourhood to speed things up:
        const int *tmp = xy_avg;
        for(int u = 0; u < samples_avg; u++)
        {
          int dx = *tmp++;
          int dy = *tmp++;
          int x = MAX(0, MIN(width - 1, t + dx));
          int y = MAX(0, MIN(height - 1, v + dy));
          local_avg += out[(size_t)y * width * ch + x * ch + 3];
        }
        avg_edge_chroma = fmax(0.01f, (float)local_avg / samples_avg);
        local_thresh = fmax(0.1f, 4.0 * d->thresh * avg_edge_chroma / MAGIC_THRESHOLD_COEFF);
      }

      if(out[(size_t)v * width * ch + t * ch + 3] > local_thresh
         // reduces artifacts ("region growing by 1 pixel"):
         || out[(size_t)MAX(0, (v - 1)) * width * ch + MAX(0, (t - 1)) * ch + 3] > local_thresh
         || out[(size_t)MAX(0, (v - 1)) * width * ch + t * ch + 3] > local_thresh
         || out[(size_t)MAX(0, (v - 1)) * width * ch + MIN(width - 1, (t + 1)) * ch + 3] > local_thresh
         || out[(size_t)v * width * ch + MAX(0, (t - 1)) * ch + 3] > local_thresh
         || out[(size_t)v * width * ch + MIN(width - 1, (t + 1)) * ch + 3] > local_thresh
         || out[(size_t)MIN(height - 1, (v + 1)) * width * ch + MAX(0, (t - 1)) * ch + 3] > local_thresh
         || out[(size_t)MIN(height - 1, (v + 1)) * width * ch + t * ch + 3] > local_thresh
         || out[(size_t)MIN(height - 1, (v + 1)) * width * ch + MIN(width - 1, (t + 1)) * ch + 3]
            > local_thresh)
      {
        float atot = 0, btot = 0;
        float norm = 0;
        float weight;
        // it seems better to use only some pixels from a larger window instead of all pixels from a smaller
        // window
        // we use a fibonacci lattice for that, samples amount need to be a fibonacci number, this can then be
        // scaled to
        // a certain radius

        // use some neighbourhood pixels for lowest chroma average
        const int *tmp = xy_small;
        for(int u = 0; u < samples_small; u++)
        {
          int dx = *tmp++;
          int dy = *tmp++;
          int x = MAX(0, MIN(width - 1, t + dx));
          int y = MAX(0, MIN(height - 1, v + dy));
          // inverse chroma weighted average of neigbouring pixels inside window
          // also taking average edge chromaticity into account (either global or local average)
          weight = 1.0 / (out[(size_t)y * width * ch + x * ch + 3] + avg_edge_chroma);
          atot += weight * in[(size_t)y * width * ch + x * ch + 1];
          btot += weight * in[(size_t)y * width * ch + x * ch + 2];
          norm += weight;
        }
        // here we could try using a "balance" between original and changed value, this could be used to
        // reduce artifcats
        // but on first tries, results weren't very convincing, and there are blend settings available anyway
        // in dt
        // float balance = (out[v*width*ch +t*ch +3]-thresh)/out[v*width*ch +t*ch +3];
        double a = (atot / norm); // *balance + in[v*width*ch + t*ch +1]*(1.0-balance);
        double b = (btot / norm); // *balance + in[v*width*ch + t*ch +2]*(1.0-balance);
        // if (a < -128.0 || a > 127.0) CLIP(a,-128.0,127.0);
        // if (b < -128.0 || b > 127.0) CLIP(b,-128.0,127.0);
        out[(size_t)v * width * ch + t * ch + 1] = a;
        out[(size_t)v * width * ch + t * ch + 2] = b;
      }
      else
      {
        out[(size_t)v * width * ch + t * ch + 1] = in[(size_t)v * width * ch + t * ch + 1];
        out[(size_t)v * width * ch + t * ch + 2] = in[(size_t)v * width * ch + t * ch + 2];
      }
      out[(size_t)v * width * ch + t * ch] = in[(size_t)v * width * ch + t * ch];
    }
  }

  if(piece->pipe->mask_display) dt_iop_alpha_copy(i, o, roi_out->width, roi_out->height);

  goto FINISH_PROCESS;

ERROR_EXIT:
  memcpy(o, i, (size_t)sizeof(float) * ch * roi_out->width * roi_out->height);

FINISH_PROCESS:
  free(xy_artifact);
  free(xy_small);
  free(xy_avg);
}