A tool to construct HDR-images from a series of exposures.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. #include <iostream>
  2. #include <fstream>
  3. #include <algorithm>
  4. #include <clocale>
  5. #include <stdlib.h>
  6. #include <time.h>
  7. #include <math.h>
  8. #include "haader.h"
  9. #define max_number(a, b) (a) > (b) ? (a) : (b)
  10. #define min_number(a, b) (a) < (b) ? (a) : (b)
  11. using namespace std;
  12. using namespace haader;
  13. int ResponseFunction::lookup(double value) const {
  14. // + 0.5 for rounding to nearest integer
  15. int bin = (double)m_lookup_table.size() * (value - m_min_value) * m_recip_range + 0.5;
  16. if (bin < 0) {
  17. return 0;
  18. } else if ((unsigned int)bin >= m_lookup_table.size()) {
  19. return 255;
  20. } else {
  21. return m_lookup_table[bin];
  22. }
  23. }
  24. void ResponseFunction::to_csv_stream(ostream &stream) const {
  25. // Set locale. Numbers should have a decimal point (not a comma)
  26. std::setlocale(LC_NUMERIC, "C");
  27. unsigned int size = m_lookup_table.size();
  28. double recip_size = 1.0 / (double)(size - 1);
  29. for (unsigned int i = 0; i < size; i++) {
  30. double x = m_min_value + i * recip_size * (m_max_value - m_min_value);
  31. int y = m_lookup_table[i];
  32. stream << x << ',' << y << endl;
  33. }
  34. }
  35. double InverseResponseFunction::lookup(int index) const {
  36. return m_lookup_table[index];
  37. }
  38. int InverseResponseFunction::inverse_lookup(double value) const {
  39. if (value <= m_lookup_table[0]) {
  40. return 0;
  41. }
  42. if (value >= m_lookup_table[255]) {
  43. return 255;
  44. }
  45. for (unsigned int i = 1; i < 256; i++) {
  46. if (value > m_lookup_table[i - 1] && value <= m_lookup_table[i]) {
  47. double diff_a = value - m_lookup_table[i - 1];
  48. double diff_b = m_lookup_table[i] - value;
  49. if (diff_a < diff_b) {
  50. return i - 1;
  51. } else {
  52. return i;
  53. }
  54. }
  55. }
  56. // should not happen
  57. return 0;
  58. }
  59. ResponseFunction InverseResponseFunction::to_response_function(int bins) {
  60. if (!is_monotonic()) {
  61. cerr << "InverseResponseFunction::to_response_function: not monotonic." << endl;
  62. return ResponseFunction();
  63. }
  64. double min_value = m_lookup_table[0];
  65. double max_value = m_lookup_table[m_lookup_table.size() - 1];
  66. ResponseFunction rf;
  67. rf.m_min_value = min_value;
  68. rf.m_max_value = max_value;
  69. rf.m_recip_range = 1.0 / (max_value - min_value);
  70. rf.m_lookup_table.resize(bins);
  71. //TODO reduce quadratic complexity to linear complexity
  72. for (int i = 0; i < bins; i++) {
  73. double y = min_value + ((max_value - min_value) * i) / (double)(bins - 1.0);
  74. rf.m_lookup_table[i] = inverse_lookup(y);
  75. }
  76. return rf;
  77. }
  78. void InverseResponseFunction::to_csv_stream(ostream &stream) const {
  79. unsigned int size = m_lookup_table.size();
  80. for (unsigned int i = 0; i < size; i++) {
  81. stream << i << ',' << m_lookup_table[i] << endl;
  82. }
  83. }
  84. bool InverseResponseFunction::repair() {
  85. // Fill missing values
  86. fill_zeros();
  87. if (is_monotonic()) {
  88. return true;
  89. }
  90. for (unsigned int i = 0; i < 128; i++) {
  91. cout << "low pass filter " << i + 1 << endl;
  92. low_pass_filter();
  93. if (is_monotonic()) {
  94. // repair success
  95. // offset values so that m_lookup_table[128] is 0.0 again
  96. double offset = -m_lookup_table[128];
  97. for (unsigned int i = 0; i < 256; i++) {
  98. m_lookup_table[i] += offset;
  99. }
  100. return true;
  101. }
  102. }
  103. // repair failed
  104. return false;
  105. }
  106. void InverseResponseFunction::fill_zeros() {
  107. //TODO test correctness
  108. // fill at front
  109. if (m_lookup_table[0] == 0) {
  110. for (unsigned int i = 0; i < 128; i++) {
  111. if (m_lookup_table[i] != 0.0) {
  112. for (unsigned int k = 0; k <= i; k++) {
  113. m_lookup_table[k] = m_lookup_table[i];
  114. }
  115. break;
  116. }
  117. }
  118. }
  119. // fill at back
  120. if (m_lookup_table[255] == 0) {
  121. for (unsigned int i = 255; i > 128; i--) {
  122. if (m_lookup_table[i] != 0.0) {
  123. for (unsigned int k = 255; k >= i; k--) {
  124. m_lookup_table[k] = m_lookup_table[i];
  125. }
  126. break;
  127. }
  128. }
  129. }
  130. {
  131. bool inside_zeros = false;
  132. int start_index = 0;
  133. for (unsigned int i = 1; i < 256; i++) {
  134. if (m_lookup_table[i] == 0.0 && !inside_zeros && i != 128) {
  135. start_index = i;
  136. inside_zeros = true;
  137. } else if ((m_lookup_table[i] != 0.0 || i == 128) && inside_zeros) {
  138. inside_zeros = false;
  139. double a_val = m_lookup_table[start_index - 1];
  140. double b_val = m_lookup_table[i];
  141. for (unsigned int k = start_index; k < i; k++) {
  142. double t = (double)(k - start_index + 1) / (double)(i - start_index + 1);
  143. m_lookup_table[k] = (1.0 - t) * a_val + t * b_val;
  144. }
  145. }
  146. }
  147. }
  148. }
  149. void InverseResponseFunction::low_pass_filter() {
  150. vector<double> newvals;
  151. newvals.resize(256);
  152. newvals[0] = m_lookup_table[0];
  153. newvals[255] = m_lookup_table[255];
  154. for (unsigned int i = 1; i < 255; i++) {
  155. newvals[i] = 0.25 * m_lookup_table[i - 1] + 0.5 * m_lookup_table[i] + 0.25 * m_lookup_table[i + 1];
  156. }
  157. for (unsigned int i = 0; i < 256; i++) {
  158. m_lookup_table[i] = newvals[i];
  159. }
  160. }
  161. bool InverseResponseFunction::is_monotonic() {
  162. for (unsigned int i = 1; i < 256; i++) {
  163. if (m_lookup_table[i - 1] > m_lookup_table[i]) {
  164. return false;
  165. }
  166. }
  167. return true;
  168. }
  169. HdrImage::HdrImage():
  170. m_width(0),
  171. m_height(0),
  172. m_components(0)
  173. {
  174. }
  175. Image HdrImage::get_log_image() {
  176. if (m_image_data.size() == 0) {
  177. return Image();
  178. }
  179. Image output;
  180. output.m_image_data.resize(m_image_data.size());
  181. output.m_width = m_width;
  182. output.m_height = m_height;
  183. output.m_components = m_components;
  184. double min_value = m_image_data[0];
  185. double max_value = m_image_data[0];
  186. unsigned int size = m_image_data.size();
  187. for (unsigned int i = 1; i < size; i++) {
  188. double val = m_image_data[i];
  189. if (val < min_value) {
  190. min_value = val;
  191. }
  192. if (val > max_value) {
  193. max_value = val;
  194. }
  195. }
  196. double scale = 256.0 / (max_value - min_value);
  197. for (unsigned int i = 0; i < size; i++) {
  198. output.m_image_data[i] = (int)((m_image_data[i] - min_value) * scale);
  199. }
  200. return output;
  201. }
  202. Image HdrImage::expose(double exposure_time, const ResponseFunction &rf, double compression) {
  203. if (m_image_data.size() == 0) {
  204. return Image();
  205. }
  206. Image output;
  207. output.m_image_data.resize(m_image_data.size());
  208. output.m_width = m_width;
  209. output.m_height = m_height;
  210. output.m_components = m_components;
  211. double log_exposure = log(exposure_time);
  212. unsigned int size = m_image_data.size();
  213. for (unsigned int i = 0; i < size; i++) {
  214. output.m_image_data[i] = rf.lookup((m_image_data[i] + log_exposure) * compression);
  215. }
  216. return output;
  217. }
  218. Histogram HdrImage::histogram(unsigned int number_of_bins) const {
  219. Histogram hist;
  220. hist.m_bins.resize(number_of_bins, 0);
  221. if (m_image_data.size() == 0) {
  222. return hist;
  223. }
  224. hist.m_min_value = m_image_data[0];
  225. hist.m_max_value = m_image_data[0];
  226. for (unsigned int i = 0; i < m_image_data.size(); i++) {
  227. double val = m_image_data[i];
  228. hist.m_min_value = min_number(hist.m_min_value, val);
  229. hist.m_max_value = max_number(hist.m_max_value, val);
  230. }
  231. double scale = (double)number_of_bins / (hist.m_max_value - hist.m_min_value);
  232. for (unsigned int i = 0; i < m_image_data.size(); i++) {
  233. double val = m_image_data[i];
  234. int bin = (val - hist.m_min_value) * scale;
  235. bin = bin < 0 ? 0 : (bin >= (int)number_of_bins ? (number_of_bins - 1) : bin);
  236. hist.m_bins[bin]++;
  237. }
  238. hist.m_max_freq = 0;
  239. for (unsigned int i = 0; i < number_of_bins; i++) {
  240. hist.m_max_freq = max_number(hist.m_max_freq, hist.m_bins[i]);
  241. }
  242. return hist;
  243. }
  244. HdrImageStack::HdrImageStack() {
  245. }
  246. bool HdrImageStack::read_from_files(char * const* file_paths, int number_of_paths) {
  247. m_images.clear();
  248. if (number_of_paths < 0) {
  249. cerr << "HdrImageStack::read_from_files: number_of_paths is negative" << endl;
  250. return false;
  251. }
  252. for (int i = 0; i < number_of_paths; i++) {
  253. m_images.push_back(Image());
  254. if (m_images[i].read_from_file(file_paths[i])) {
  255. cout << "Read \"" << file_paths[i] << "\"" << endl;
  256. if (!m_images[0].has_equal_dimensions(m_images[i])) {
  257. cerr << "HdrImageStack::read_from_files: Dimensions do not match (\""
  258. << file_paths[i]
  259. << "\")"
  260. << endl;
  261. m_images.clear();
  262. return false;
  263. }
  264. if (m_images[i].get_exposure_time() == 0.0) {
  265. cerr << "HdrImageStack::read_from_files: Could not get exposure time (\""
  266. << file_paths[i]
  267. << "\")"
  268. << endl;
  269. m_images.clear();
  270. return false;
  271. }
  272. } else {
  273. m_images.clear();
  274. return false;
  275. }
  276. }
  277. sort_by_exposure();
  278. return true;
  279. }
  280. bool HdrImageStack::add_from_file_path(const char* file_path) {
  281. unsigned int i = m_images.size();
  282. m_images.push_back(Image());
  283. if (m_images[i].read_from_file(file_path)) {
  284. cout << "Read \"" << file_path << "\"" << endl;
  285. if (!m_images[0].has_equal_dimensions(m_images[i])) {
  286. cerr << "HdrImageStack::add_from_file_path: Dimensions do not match (\""
  287. << file_path
  288. << "\")"
  289. << endl;
  290. return false;
  291. }
  292. if (m_images[i].get_exposure_time() == 0.0) {
  293. cerr << "HdrImageStack::read_from_files: Could not get exposure time (\""
  294. << file_path
  295. << "\")"
  296. << endl;
  297. return false;
  298. }
  299. } else {
  300. return false;
  301. }
  302. sort_by_exposure();
  303. return true;
  304. }
  305. bool HdrImageStack::get_average_image_slow(Image &output) {
  306. if (m_images.size() == 0) {
  307. return false;
  308. }
  309. output.m_image_data.resize(m_images[0].m_image_data.size());
  310. output.m_width = m_images[0].m_width;
  311. output.m_height = m_images[0].m_height;
  312. output.m_components = m_images[0].m_components;
  313. unsigned int bytes_size = m_images[0].m_image_data.size();
  314. unsigned int images_size = m_images.size();
  315. for (unsigned int i = 0; i < bytes_size; i++) {
  316. int v = 0;
  317. for (unsigned int k = 0; k < images_size; k++) {
  318. v += m_images[k].m_image_data[i];
  319. }
  320. output.m_image_data[i] = v / (int)images_size;
  321. }
  322. return true;
  323. }
  324. bool HdrImageStack::get_average_image(Image &output) {
  325. if (m_images.size() == 0) {
  326. return false;
  327. }
  328. output.m_image_data.resize(m_images[0].m_image_data.size());
  329. output.m_width = m_images[0].m_width;
  330. output.m_height = m_images[0].m_height;
  331. output.m_components = m_images[0].m_components;
  332. unsigned int bytes_size = m_images[0].m_image_data.size();
  333. unsigned int images_size = m_images.size();
  334. const unsigned int buf_size = 128;
  335. int buf[buf_size];
  336. for (unsigned int i = 0; i + buf_size <= bytes_size; i += buf_size) {
  337. for (unsigned int m = 0; m < buf_size; m++) {
  338. buf[m] = m_images[0].m_image_data[i + m];
  339. }
  340. for (unsigned int k = 1; k < images_size; k++) {
  341. for (unsigned int m = 0; m < buf_size; m++) {
  342. buf[m] += m_images[k].m_image_data[i + m];
  343. }
  344. }
  345. for (unsigned int m = 0; m < buf_size; m++) {
  346. output.m_image_data[i + m] = buf[m] / images_size;
  347. }
  348. }
  349. // TODO test this with an image that has prime numbered dimensions
  350. unsigned int rest = bytes_size % buf_size;
  351. for (unsigned int i = bytes_size - rest; i < bytes_size; i++) {
  352. int v = 0;
  353. for (unsigned int k = 0; k < images_size; k++) {
  354. v += m_images[k].m_image_data[i];
  355. }
  356. output.m_image_data[i] = v / (int)images_size;
  357. }
  358. return true;
  359. }
  360. bool HdrImageStack::get_inverse_response_function(InverseResponseFunction &output, unsigned int num_samples) {
  361. if (m_images.size() == 0) {
  362. cerr << "HdrImageStack::get_inverse_response_function: There are no images." << endl;
  363. return false;
  364. }
  365. srand(time(0));
  366. unsigned int images_size = m_images.size();
  367. unsigned int c = m_images[0].m_components;
  368. unsigned int width = m_images[0].m_width;
  369. unsigned int height = m_images[0].m_height;
  370. vector<vector<double> > resp;
  371. resp.resize(256);
  372. for (unsigned int i = 0; i < num_samples; i++) {
  373. vector<int> values;
  374. vector<double> exposure_times;
  375. unsigned int x = rand() % width;
  376. unsigned int y = rand() % height;
  377. unsigned int index = (y * width + x) * c;
  378. for (unsigned int k = 0; k < images_size; k++) {
  379. int val = m_images[k].m_image_data[index];
  380. if (val == 255) {
  381. break;
  382. }
  383. values.push_back(val);
  384. exposure_times.push_back(log(m_images[k].get_exposure_time()));
  385. }
  386. if (values.size() > 0 && values[0] < 128 && values[values.size() - 1] > 128) {
  387. double offset = 0.0;
  388. for (unsigned int k = 0; k < values.size(); k++) {
  389. if (values[k] == 128) {
  390. offset = -exposure_times[k];
  391. break;
  392. } else if (values[k] > 128) {
  393. double delta_val = values[k] - values[k -1];
  394. double t = (double)(128 - values[k - 1]) / delta_val;
  395. offset = -((1.0 - t) * exposure_times[k - 1] + t * exposure_times[k]);
  396. break;
  397. }
  398. }
  399. for (unsigned int k = 0; k < values.size(); k++) {
  400. resp[values[k]].push_back(exposure_times[k] + offset);
  401. }
  402. }
  403. }
  404. output.m_lookup_table.resize(256);
  405. for (unsigned int i = 0; i < 256; i++) {
  406. double avg = 0.0;
  407. for (unsigned int k = 0; k < resp[i].size(); k++) {
  408. avg += resp[i][k];
  409. }
  410. if (resp[i].size() > 0) {
  411. output.m_lookup_table[i] = avg / (double)resp[i].size();
  412. } else {
  413. output.m_lookup_table[i] = 0.0;
  414. }
  415. }
  416. for (unsigned int i = 0; i < 256; i++) {
  417. double avg = 0.0;
  418. for (unsigned int k = 0; k < resp[i].size(); k++) {
  419. avg += resp[i][k];
  420. }
  421. if (resp[i].size() > 0) {
  422. output.m_lookup_table[i] = avg / (double)resp[i].size();
  423. } else {
  424. output.m_lookup_table[i] = 0.0;
  425. }
  426. }
  427. return output.repair();
  428. }
  429. HdrImage HdrImageStack::get_hdr_image(const InverseResponseFunction &invRespFunc) {
  430. if (m_images.size() == 0) {
  431. return HdrImage();
  432. }
  433. HdrImage output;
  434. output.m_image_data.resize(m_images[0].m_image_data.size());
  435. output.m_width = m_images[0].m_width;
  436. output.m_height = m_images[0].m_height;
  437. output.m_components = m_images[0].m_components;
  438. unsigned int bytes_size = m_images[0].m_image_data.size();
  439. unsigned int images_size = m_images.size();
  440. // precompute log of exposure times
  441. vector<double> log_exposures;
  442. log_exposures.resize(images_size);
  443. for (unsigned int k = 0; k < images_size; k++) {
  444. log_exposures[k] = log(m_images[k].get_exposure_time());
  445. }
  446. for (unsigned int i = 0; i < bytes_size; i++) {
  447. double scale_sum = 0.0;
  448. double output_val = 0.0;
  449. for (unsigned int k = 0; k < images_size; k++) {
  450. int val = m_images[k].m_image_data[i];
  451. double scale = 1.0 - fabs((double)(128.0 - val) * (1.0 / 128.0));
  452. output_val += scale * (invRespFunc.lookup(val) - log_exposures[k]);
  453. scale_sum += scale;
  454. }
  455. output.m_image_data[i] = output_val / scale_sum;
  456. }
  457. return output;
  458. }
  459. static bool compare_image_by_exposure(const Image &a, const Image &b) {
  460. return a.get_exposure_time() < b.get_exposure_time();
  461. }
  462. void HdrImageStack::sort_by_exposure() {
  463. std::sort(m_images.begin(), m_images.end(), compare_image_by_exposure);
  464. }