index.js 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import { isNodePattern } from '@jimp/utils';
  2. /**
  3. * Get an image's histogram
  4. * @return {object} An object with an array of color occurrence counts for each channel (r,g,b)
  5. */
  6. function histogram() {
  7. const histogram = {
  8. r: new Array(256).fill(0),
  9. g: new Array(256).fill(0),
  10. b: new Array(256).fill(0)
  11. };
  12. this.scanQuiet(0, 0, this.bitmap.width, this.bitmap.height, function(
  13. x,
  14. y,
  15. index
  16. ) {
  17. histogram.r[this.bitmap.data[index + 0]]++;
  18. histogram.g[this.bitmap.data[index + 1]]++;
  19. histogram.b[this.bitmap.data[index + 2]]++;
  20. });
  21. return histogram;
  22. }
  23. /**
  24. * Normalize values
  25. * @param {integer} value Pixel channel value.
  26. * @param {integer} min Minimum value for channel
  27. * @param {integer} max Maximum value for channel
  28. * @return {integer} normalized values
  29. */
  30. const normalize = function(value, min, max) {
  31. return ((value - min) * 255) / (max - min);
  32. };
  33. const getBounds = function(histogramChannel) {
  34. return [
  35. histogramChannel.findIndex(value => value > 0),
  36. 255 -
  37. histogramChannel
  38. .slice()
  39. .reverse()
  40. .findIndex(value => value > 0)
  41. ];
  42. };
  43. /**
  44. * Normalizes the image
  45. * @param {function(Error, Jimp)} cb (optional) a callback for when complete
  46. * @returns {Jimp} this for chaining of methods
  47. */
  48. export default () => ({
  49. normalize(cb) {
  50. const h = histogram.call(this);
  51. // store bounds (minimum and maximum values)
  52. const bounds = {
  53. r: getBounds(h.r),
  54. g: getBounds(h.g),
  55. b: getBounds(h.b)
  56. };
  57. // apply value transformations
  58. this.scanQuiet(0, 0, this.bitmap.width, this.bitmap.height, function(
  59. x,
  60. y,
  61. idx
  62. ) {
  63. const r = this.bitmap.data[idx + 0];
  64. const g = this.bitmap.data[idx + 1];
  65. const b = this.bitmap.data[idx + 2];
  66. this.bitmap.data[idx + 0] = normalize(r, bounds.r[0], bounds.r[1]);
  67. this.bitmap.data[idx + 1] = normalize(g, bounds.g[0], bounds.g[1]);
  68. this.bitmap.data[idx + 2] = normalize(b, bounds.b[0], bounds.b[1]);
  69. });
  70. if (isNodePattern(cb)) {
  71. cb.call(this, null, this);
  72. }
  73. return this;
  74. }
  75. });