String S = "./Documents/Processing/tight_gray/"; File[] F;
int w, h;                              int t = 0;
static final int d = 3, e = 24;        boolean b = false;
int rxm, gxm, rxl, gxl;                float rym, gym, ryl, gyl;
IntList KX;                            FloatList KY;
float[][][] X, Y, T;                   PImage I, J;

import java.util.Arrays;

void setup() { size(800, 800); colorMode(RGB, 256); background(200);
surface.setTitle("Tight Field"); File D = new File(S); File[] G = D.listFiles();
int k = 0; for (File g : G) {
  if (g.getName().endsWith(".jpg") || g.getName().endsWith(".jpeg")) { k++; } }
F = new File[k]; k = 0; for (File g : G) {
  if (g.getName().endsWith(".jpg") || g.getName().endsWith(".jpeg")) { F[k]=g; k++; } }
Arrays.sort(F); if (F.length == 0) { exit(); } }

void draw() { I = loadImage(S + F[t].getName());
w = I.width; h = I.height; J = createImage(w, h, RGB); w = max(w, h); h = w;
X = new float[w][h][d]; Y = new float[w][h][d]; T = new float[w][h][d];
for (int j = 0; j < I.height; j++) { for (int i = 0; i < I.width; i++) {
  color lab = rgb2lab(I.get(i, j)); I.set(i, j, lab);
  X[i][j][0] = red(lab) -  ceil(e/2.0); X[i][j][1] = red(lab);
  X[i][j][2] = red(lab) + floor(e/2.0);
  Y[i][j][0] = X[i][j][0]; Y[i][j][1] = X[i][j][1]; Y[i][j][2] = X[i][j][2]; } }
tighten(X); flip(X); tighten(X); flip(X);
flip(Y); tighten(Y); flip(Y); tighten(Y);
for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++) { float c = red(I.get(i, j));
  if (abs(c - X[i][j][1]) > abs(c - Y[i][j][1])) { X[i][j][1] = Y[i][j][1]; } } }
for (int j = 0; j < I.height; j++) { for (int i = 0; i < I.width; i++) {
  J.set(i, j, color((X[i][j][1] + red(I.get(i, j)))/2.0)); } }
J.save(F[t].getName().substring(0, F[t].getName().length() - 4) + ".tif");
t++; if (t == F.length) { exit(); } }

void tighten(float[][][] X) { for (int g = 0; g < h; g++) {
  KX = new IntList(); KX.append(0); KY = new FloatList(); KY.append(X[0][g][1]);
  rxm = 0; rym = X[rxm][g][2]; rxl = rxm; ryl = rym;
  gxm = 0; gym = X[gxm][g][0]; gxl = gxm; gyl = gym;
  while (gxl < w-2) {
    rxl++; float rs = (rym - KY.get(KY.size() - 1))/(rxm - KX.get(KX.size() - 1));
    ryl = KY.get(KY.size() - 1) + rs*(rxl - KX.get(KX.size() - 1));
    gxl++; float gs = (gym - KY.get(KY.size() - 1))/(gxm - KX.get(KX.size() - 1));
    gyl = KY.get(KY.size() - 1) + gs*(gxl - KX.get(KX.size() - 1));
    if        (X[rxl][g][1] <= gyl) { b = true; KX.append(gxm); KY.append(gym);
      rxm = gxm; rym = gym; rxl = gxm; ryl = gym; gxl = gxm; gyl = gym;
    } else if (X[gxl][g][1] >= ryl) { b = true; KX.append(rxm); KY.append(rym);
      gxm = rxm; gym = rym; gxl = rxm; gyl = rym; rxl = rxm; ryl = rym;
    } else if (X[rxl][g][1] <=  ryl) { rxm = rxl; rym = X[rxl][g][1]; ryl = rym;
    } else if (X[gxl][g][1] >=  gyl) { gxm = gxl; gym = X[gxl][g][1]; gyl = gym;
    } else if (gxl == w-1) { b = true; KX.append(gxl); KY.append((ryl+gyl)/2.0); }
    if (b) { int s = KX.size() - 2; for (int q = KX.get(s); q < KX.get(s+1); q++) {
      float m = (KY.get(s+1) - KY.get(s))/(KX.get(s+1) - KX.get(s));
      X[q][g][1] = m*(q - KX.get(s)) + KY.get(s);
      float p = min(X[q][g][2] - X[q][g][1], X[q][g][1] - X[q][g][0]);
      X[q][g][0] = X[q][g][1] - p; X[q][g][2] = X[q][g][1] + p; } b = false; } } } }
    
void flip(float[][][] X) {
  for (int k = 0; k < d; k++) { for (int j = 0; j < h; j++) {
    for (int i = 0; i < w; i++) { T[j][i][k] = X[i][j][k]; } } }
  for (int k = 0; k < d; k++) { for (int j = 0; j < h; j++) {
    for (int i = 0; i < w; i++) { X[i][j][k] = T[i][j][k]; } } } }

color rgb2lab(color rgb) { float[] X = new float[3];
  float[] C = { red(rgb)/255.0, green(rgb)/255.0, blue(rgb)/255.0 };
  for (int i = 0; i < 3; i++) { if (C[i] <= 0.0405) { C[i] = C[i]/12.92;
    } else { C[i] = pow((C[i] + 0.055)/1.055, 2.4); } }
  X[0] = (0.4124*C[0] + 0.3576*C[1] + 0.1805*C[2]) / 0.95047;
  X[1] =  0.2126*C[0] + 0.7152*C[1] + 0.0722*C[2];
  X[2] = (0.0193*C[0] + 0.1192*C[1] + 0.9505*C[2]) / 1.08883;
  for (int i = 0; i < 3; i++) { if (X[i] > 0.008856) { X[i] = pow(X[i], 1.0/3.0);
    } else { X[i] = 7.787*X[i] + 16.0/116.0; } }
  return color(min(255.0, max(0.0, 2.55*(116.0*X[1] - 16.0))),
               min(255.0, max(0.0, 2.55*(500.0*(X[0] - X[1])))),
               min(255.0, max(0.0, 2.55*(200.0*(X[1] - X[2]))))); }