1 module rip.analysis.histogram;
2 
3 private {
4     import std.algorithm;
5     import std.range;
6     import std.math;
7 
8     import rip.concepts.color;
9     import rip.concepts.surface;
10     import rip.concepts.channel;
11     import rip.concepts.ranges : toSurface;
12 }
13 
14 struct Histogram {
15     uint[]   data;
16 
17     const Channel channel;
18 
19     alias data this;
20 
21     this(Channel channel) {
22         this.channel = channel;
23         data = new uint[channel.getRangeSize!uint()];
24     }
25 }
26 
27 Histogram takeHistogram(Range)(in Range pixelRange, Channel channel)
28         if(isPixelRange!Range)
29 {
30     auto histogram = Histogram(channel);
31 
32     pixelRange
33 //        .getPixels()
34         .each!(
35             (color) => histogram[ channel.getIndex(color) ]++
36         );
37 
38     return histogram;
39 }
40 
41 //std.algorithm.max не приспособлен для диапазонов
42 //TODO: убрать это отсюда. Ну позязя :p
43 T maxValue(T)(T[] args...) {
44     T max = args[0];
45 
46     foreach(value; args) {
47         if(value > max)
48             max = value;
49     }
50 
51     return max;
52 }
53 
54 Surface drawHistogram(  Histogram histogram,
55                         RGBColor background = new RGBColor(255, 255, 255),
56                         RGBColor pencil = new RGBColor(0, 0, 0)) {
57     uint width = histogram.channel.getRangeSize!uint;
58     uint height = cast(uint)
59         (maxValue(histogram.data) / histogram.channel.getRangeSize!float);
60 
61     auto surface = new Surface(width, height);
62 
63     foreach(i; 0..width) {
64         foreach(j; 0..height) {
65             if(j <= histogram[i] / histogram.channel.getRangeSize!float()) {
66                 surface[i, height - j - 1] = pencil;
67             }
68             else {
69                 surface[i, height - j - 1] = background;
70             }
71         }
72     }
73 
74     return surface;
75 }
76 
77 auto  equalizeHistogram(Histogram histogram) {
78     Histogram equalizedHistogram = histogram;
79 
80     uint pixelsNumber = histogram.data.reduce!"a + b";
81 
82     auto probability = histogram
83                             .data
84                             .map!(( float a)  => a / pixelsNumber)
85                             .array;
86 
87     int i = 1;
88     foreach(ref el; probability[1..$-1]) {
89         el += probability[i-1];
90         i++;
91     }
92 
93     uint size = histogram.channel.getRangeSize!uint;
94 
95     equalizedHistogram = probability.map!(a => cast(uint)(a * size)).array;
96     return equalizedHistogram;
97 }
98 
99 Surface appearHistogramToSurface(Histogram histogram, Surface sur) {
100     import rip.concepts;
101 
102     auto channel = histogram.channel;
103 
104     auto getNewColor(in RGBColor color) {
105         return channel.injectValue(color, channel.getValue(color));
106     }
107 
108     return sur
109         //функция ниже отзеркаливает картинку по диагонали
110         .createFences(1,1)
111         .map!(a => a.front)
112         .map!getNewColor
113         //функция ниже отзеркаливает картинку по диагонали
114         .toSurface(sur.getWidth!size_t, sur.getHeight!size_t);
115 }