Image transformations

by IceCreamYou

HTML

<div>
  <div><strong>Input</strong></div>
  <canvas id="input" width="300" height="70"></canvas>
</div>

<div>
  <div><strong>Outputs</strong></div>
  <div>
    <span class="label">Averaging:</span>
    <canvas id="output-avg" width="300" height="70"></canvas>
  </div>
  <div>
    <span class="label">Luminance:</span>
    <canvas id="output-lum" width="300" height="70"></canvas>
  </div>
  <div>
    <span class="label">Gamma-corrected luminance:</span>
    <canvas id="output-gamma-lum" width="300" height="70"></canvas>
  </div>
  <div>
    <span class="label">Desaturation:</span>
    <canvas id="output-des" width="300" height="70"></canvas>
  </div>
  <div>
    <span class="label">Max decomposition:</span>
    <canvas id="output-max" width="300" height="70"></canvas>
  </div>
  <div>
    <span class="label">Min decomposition:</span>
    <canvas id="output-min" width="300" height="70"></canvas>
  </div>
  <div>
    <span class="label">Red channel:</span>
    <canvas id="output-r" width="300" height="70"></canvas>
  </div>
  <div>
    <span class="label">Green channel:</span>
    <canvas id="output-g" width="300" height="70"></canvas>
  </div>
  <div>
    <span class="label">Blue channel:</span>
    <canvas id="output-b" width="300" height="70"></canvas>
  </div>
  <div>
    <span class="label">Contrast:</span>
    <canvas id="output-contrast" width="300" height="70"></canvas>
  </div>
  <div>
    <span class="label">Contrast v2:</span>
    <canvas id="output-contrast2" width="300" height="70"></canvas>
  </div>
  <div>
    <span class="label">Brightness:</span>
    <canvas id="output-brightness" width="300" height="70"></canvas>
  </div>
  <div>
    <span class="label">Invert:</span>
    <canvas id="output-invert" width="300" height="70"></canvas>
  </div>
</div>

CSS

canvas {
  border: 1px solid black;
}

.label {
  display: inline-block;
  width: 100px;
}

JavaScript

/**
 * Gets a random integer between `lo` and `hi`.
 */
function getRandIntBetween(lo, hi) {
		return Math.floor(Math.random() * (hi - lo + 1) + lo);
}

/**
 * Logs a string to the DOM for debugging.
 */
function log(s) {
		var d = document.createElement('div');
    d.textContent = s;
    document.body.appendChild(d);
}

/* Prepare an interesting input */

var canvas = document.getElementById('input'),
		context = canvas.getContext('2d'),
	  w = canvas.width,
  	h = canvas.height,
    i,
    l,
    avg;

// Only previously touched pixels are modifiable when writing imageData
context.fillRect(0, 0, w, h);

var imageData = context.getImageData(0, 0, w, h),
    pixels = imageData.data;

// Set random colors. Picking random colors in RGB isn't very pretty,
// but it suffices for the purposes of this example.
for (i = 0, l = pixels.length; i < l; i += 4) {
		do {
        pixels[i] = getRandIntBetween(0, 255);
        pixels[i + 1] = getRandIntBetween(0, 255);
        pixels[i + 2] = getRandIntBetween(0, 255);
    }
    while (Math.abs(pixels[i] - pixels[i+1]) + Math.abs(pixels[i+1] - pixels[i+2]) + Math.abs(pixels[i+2] - pixels[i]) < 0);
}

var p = pixels.slice();

context.putImageData(imageData, 0, 0);

/* Averaging method */

for (i = 0, l = pixels.length; i < l; i += 4) {
		avg = (p[i] + p[i+1] + p[i+2]) / 3;
    pixels[i] = pixels[i+1] = pixels[i+2] = avg;
}

document.getElementById('output-avg').getContext('2d').putImageData(imageData, 0, 0);

/* Luminance method per the ITU-R BT.709 standard */

for (i = 0, l = pixels.length; i < l; i += 4) {
		avg = p[i] * 0.2126 + p[i+1] * 0.7152 + p[i+2] * 0.0722;
    pixels[i] = pixels[i+1] = pixels[i+2] = avg;
}

document.getElementById('output-lum').getContext('2d').putImageData(imageData, 0, 0);

/* Gamma-corrected luminance method for the sRGB color space */

function rgbToLinear(x) {
		return x <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);
}

for (i = 0, l = pixels.length; i < l; i += 4) {
		avg =...