forked from 26hzhang/OptimizedImageEnhance
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ALTMRetinex.java
executable file
·129 lines (119 loc) · 4.72 KB
/
ALTMRetinex.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package com.isaac.models;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import com.isaac.utils.Filters;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
public class ALTMRetinex {
// Global Adaptation Parameters -- fixed
private static final double rParam = 0.299;
private static final double gParam = 0.587;
private static final double bParam = 0.114;
public static Mat enhance(Mat image, int r, double eps, double eta, double lambda, double krnlRatio) {
image.convertTo(image, CvType.CV_32F);
// extract each color channel
List<Mat> bgr = new ArrayList<>();
Core.split(image, bgr);
Mat bChannel = bgr.get(0);
Mat gChannel = bgr.get(1);
Mat rChannel = bgr.get(2);
int m = rChannel.rows();
int n = rChannel.cols();
// Global Adaptation
List<Mat> list = globalAdaptation(bChannel, gChannel, rChannel, m, n);
Mat Lw = list.get(0);
Mat Lg = list.get(1);
// Local Adaptation
Mat Hg = localAdaptation(Lg, m, n, r, eps, krnlRatio);
Lg.convertTo(Lg, CvType.CV_32F);
// process
Mat alpha = new Mat(m, n, rChannel.type());
Core.divide(Lg, new Scalar(Core.minMaxLoc(Lg).maxVal / eta), alpha);
//Core.multiply(alpha, new Scalar(eta), alpha);
Core.add(alpha, new Scalar(1.0), alpha);
//alpha = adjustment(alpha, 1.25);
Mat Lg_ = new Mat(m, n, rChannel.type());
Core.add(Lg, new Scalar(1.0 / 255.0), Lg_);
Core.log(Lg_, Lg_);
double beta = Math.exp(Core.sumElems(Lg_).val[0] / (m * n)) * lambda;
Mat Lout = new Mat(m, n, rChannel.type());
Core.divide(Lg, Hg, Lout);
Core.add(Lout, new Scalar(beta), Lout);
Core.log(Lout, Lout);
Core.normalize(alpha.mul(Lout), Lout, 0, 255, Core.NORM_MINMAX);
Mat gain = obtainGain(Lout, Lw, m, n);
// output
Core.divide(rChannel.mul(gain), new Scalar(Core.minMaxLoc(rChannel).maxVal / 255.0), rChannel); // Red Channel
Core.divide(gChannel.mul(gain), new Scalar(Core.minMaxLoc(gChannel).maxVal / 255.0), gChannel); // Green Channel
Core.divide(bChannel.mul(gain), new Scalar(Core.minMaxLoc(bChannel).maxVal / 255.0), bChannel); // Blue Channel
// merge three color channels to a image
Mat outval = new Mat();
Core.merge(new ArrayList<>(Arrays.asList(bChannel, gChannel, rChannel)), outval);
outval.convertTo(outval, CvType.CV_8UC1);
return outval;
}
private static Mat localAdaptation(Mat Lg, int rows, int cols, int r, double eps, double krnlRatio) {
int krnlSz = Stream.of(3.0, rows * krnlRatio, cols * krnlRatio).max(Double::compare).orElse(3.0).intValue();
// maximum filter: using dilate to extract the local maximum of a image block, which acts as the maximum filter
// Meanwhile, minimum filter can be achieved by using erode function
Mat Lg_ = new Mat();
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(krnlSz, krnlSz), new Point(-1, -1));
Imgproc.dilate(Lg, Lg_, kernel);
// guided image filter
return Filters.GuidedImageFilter(Lg, Lg_, r, eps);
}
private static List<Mat> globalAdaptation(Mat b, Mat g, Mat r, int rows, int cols) {
// Calculate Lw & maximum of Lw
Mat Lw = new Mat(rows, cols, r.type());
Core.multiply(r, new Scalar(rParam), r);
Core.multiply(g, new Scalar(gParam), g);
Core.multiply(b, new Scalar(bParam), b);
Core.add(r, g, Lw);
Core.add(Lw, b, Lw);
double LwMax = Core.minMaxLoc(Lw).maxVal; // the maximum luminance value
// Calculate log-average luminance and get global adaptation result
Mat Lw_ = Lw.clone();
Core.add(Lw_, new Scalar(0.001), Lw_);
Core.log(Lw_, Lw_);
double LwAver = Math.exp(Core.sumElems(Lw_).val[0] / (rows * cols));
Mat Lg = Lw.clone();
Core.divide(Lg, new Scalar(LwAver), Lg);
Core.add(Lg, new Scalar(1.0), Lg);
Core.log(Lg, Lg);
Core.divide(Lg, new Scalar(Math.log(LwMax / LwAver + 1.0)), Lg); // Lg is the global adaptation
List<Mat> list = new ArrayList<>();
list.add(Lw);
list.add(Lg);
return list;
}
private static Mat obtainGain(Mat Lout, Mat Lw, int rows, int cols) {
Mat gain = new Mat(rows, cols, Lout.type());
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (Lw.get(i, j)[0] == 0) gain.put(i, j, Lout.get(i, j)[0]);
else gain.put(i, j, Lout.get(i, j)[0] / Lw.get(i, j)[0]);
}
}
return gain;
}
@SuppressWarnings("unused")
private static Mat adjustment(Mat alpha, double a) {
double b = Core.minMaxLoc(alpha).maxVal;
int rows = alpha.rows();
int cols = alpha.cols();
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
//double val = alpha.get(i, j)[0];
alpha.put(i, j, (2 * Math.atan(a * alpha.get(i, j)[0] / b) / Math.PI * b));
}
}
return alpha;
}
}