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

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