-
-
Notifications
You must be signed in to change notification settings - Fork 252
/
Copy pathsolution.cpp
126 lines (106 loc) · 3.42 KB
/
solution.cpp
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
#include "solution.h"
#include <algorithm>
#include <fstream>
#include <stdint.h>
#include <cmath>
#include <ios>
// ******************************************
// ONLY THE FOLLOWING FUNCTION IS BENCHMARKED
// Compute the histogram of image pixels
std::array<uint32_t, 256> computeHistogram(const GrayscaleImage& image) {
std::array<uint32_t, 256> hist;
hist.fill(0);
for (int i = 0; i < image.width * image.height; ++i)
hist[image.data[i]]++;
return hist;
}
// ******************************************
// Calculate Otsu's Threshold
int calcOtsuThreshold(const std::array<uint32_t, 256>& hist, int totalPixels) {
// normalize histogram
std::array<double, 256> normHist;
for (int i = 0; i < 256; ++i)
normHist[i] = (double)hist[i] / totalPixels;
double maxVariance = 0;
int optimalThreshold = 0;
// Find the optimal threshold
for (int t = 0; t < 256; ++t) {
double weight1 = 0, weight2 = 0, mean1 = 0, mean2 = 0;
for (int i = 0; i <= t; ++i) {
weight1 += normHist[i];
mean1 += i * normHist[i];
}
for (int i = t + 1; i < 256; ++i) {
weight2 += normHist[i];
mean2 += i * normHist[i];
}
if (weight1 == 0 || weight2 == 0) continue;
mean1 /= weight1;
mean2 /= weight2;
double variance = weight1 * weight2 * std::pow(mean1 - mean2, 2);
if (variance > maxVariance) {
maxVariance = variance;
optimalThreshold = t;
}
}
return optimalThreshold;
}
// Function to apply the threshold to create a binary image
void applyOtsuThreshold(GrayscaleImage& image) {
// Compute the histogram
std::array<uint32_t, 256> hist = computeHistogram(image);
auto totalPixels = image.height * image.width;
int threshold = calcOtsuThreshold(hist, totalPixels);
// Apply Otsu's thresholding
for (int i = 0; i < totalPixels; ++i)
image.data[i] = (image.data[i] >= threshold) ? 255 : 0;
}
// Loads GrayscaleImage image. Format is
// https://people.sc.fsu.edu/~jburkardt/data/pgmb/pgmb.html
bool GrayscaleImage::load(const std::string &filename, const int maxSize) {
data.reset();
std::ifstream input(filename.data(),
std::ios_base::in | std::ios_base::binary);
if (input.is_open()) {
std::string line;
input >> line;
if (line == "P5") {
int amplitude;
input >> width >> height >> amplitude;
char c;
input.unsetf(std::ios_base::skipws);
input >> c;
if (c == '\r')
input >> c;
if ((width > 0) && (width <= maxSize) && (height > 0) &&
(height <= maxSize) && (amplitude >= 0) && (amplitude <= 255) &&
(c == '\n')) {
size = static_cast<size_t>(width) * static_cast<size_t>(height);
data.reset(new uint8_t[size]);
if (data) {
input.read(reinterpret_cast<char *>(data.get()), size);
if (input.fail()) {
data.reset();
}
}
}
}
input.close();
}
return !!data;
}
// Saves GrayscaleImage image. Format is
// https://people.sc.fsu.edu/~jburkardt/data/pgmb/pgmb.html
void GrayscaleImage::save(const std::string &filename) {
std::ofstream output(filename.data(),
std::ios_base::out | std::ios_base::binary);
if (output.is_open()) {
output << "P5" << std::endl
<< width << ' ' << height << std::endl
<< "255" << std::endl;
if (data) {
output.write(reinterpret_cast<const char *>(data.get()), size);
}
output.close();
}
}