You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

image.c 10 KiB

21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
20 years ago
21 years ago
20 years ago
20 years ago
21 years ago
20 years ago
20 years ago
21 years ago
20 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. /*
  2. * This file is part of Aptdec.
  3. * Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019-2020
  4. *
  5. * Aptdec is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. *
  18. */
  19. #include <stdio.h>
  20. #include <string.h>
  21. #include <sndfile.h>
  22. #include <math.h>
  23. #include <stdlib.h>
  24. #include "common.h"
  25. #include "offsets.h"
  26. #define REGORDER 3
  27. typedef struct {
  28. double cf[REGORDER + 1];
  29. } rgparam_t;
  30. extern void polyreg(const int m, const int n, const double x[], const double y[], double c[]);
  31. // Compute regression
  32. static void rgcomp(double x[16], rgparam_t * rgpr) {
  33. // { 0.106, 0.215, 0.324, 0.433, 0.542, 0.652, 0.78, 0.87, 0.0 }
  34. const double y[9] = { 31.07, 63.02, 94.96, 126.9, 158.86, 191.1, 228.62, 255.0, 0.0 };
  35. polyreg(REGORDER, 9, x, y, rgpr->cf);
  36. }
  37. // Convert a value to 0-255 based off the provided regression curve
  38. static double rgcal(float x, rgparam_t *rgpr) {
  39. double y, p;
  40. int i;
  41. for (i = 0, y = 0.0, p = 1.0; i < REGORDER + 1; i++) {
  42. y += rgpr->cf[i] * p;
  43. p = p * x;
  44. }
  45. return y;
  46. }
  47. static double tele[16];
  48. static double Cs;
  49. void histogramEqualise(float **prow, int nrow, int offset, int width){
  50. // Plot histogram
  51. int histogram[256] = { 0 };
  52. for(int y = 0; y < nrow; y++)
  53. for(int x = 0; x < width; x++)
  54. histogram[(int)CLIP(prow[y][x+offset], 0, 255)]++;
  55. // Calculate cumulative frequency
  56. long sum = 0, cf[256] = { 0 };
  57. for(int i = 0; i < 255; i++){
  58. sum += histogram[i];
  59. cf[i] = sum;
  60. }
  61. // Apply histogram
  62. int area = nrow * width;
  63. for(int y = 0; y < nrow; y++){
  64. for(int x = 0; x < width; x++){
  65. int k = prow[y][x+offset];
  66. prow[y][x+offset] = (256.0/area) * cf[k];
  67. }
  68. }
  69. }
  70. void linearEnhance(float **prow, int nrow, int offset, int width){
  71. // Plot histogram
  72. int histogram[256] = { 0 };
  73. for(int y = 0; y < nrow; y++)
  74. for(int x = 0; x < width; x++)
  75. histogram[(int)CLIP(prow[y][x+offset], 0, 255)]++;
  76. // Find min/max points
  77. int min = -1, max = -1;
  78. for(int i = 5; i < 250; i++){
  79. if(histogram[i]/width/(nrow/255.0) > 0.25){
  80. if(min == -1) min = i;
  81. max = i;
  82. }
  83. }
  84. // Stretch the brightness into the new range
  85. for(int y = 0; y < nrow; y++)
  86. for(int x = 0; x < width; x++)
  87. prow[y][x+offset] = (prow[y][x+offset]-min) / (max-min) * 255.0;
  88. }
  89. // Brightness calibrate, including telemetry
  90. void calibrateImage(float **prow, int nrow, int offset, int width, rgparam_t regr){
  91. offset -= SYNC_WIDTH+SPC_WIDTH;
  92. for (int y = 0; y < nrow; y++) {
  93. for (int x = 0; x < width+SYNC_WIDTH+SPC_WIDTH+TELE_WIDTH; x++) {
  94. float pv = rgcal(prow[y][x + offset], &regr);
  95. prow[y][x + offset] = CLIP(pv, 0, 255);
  96. }
  97. }
  98. }
  99. double teleNoise(double wedges[16]){
  100. double pattern[9] = { 31.07, 63.02, 94.96, 126.9, 158.86, 191.1, 228.62, 255.0, 0.0 };
  101. double noise = 0;
  102. for(int i = 0; i < 9; i++)
  103. noise += fabs(wedges[i] - pattern[i]);
  104. return noise;
  105. }
  106. // Get telemetry data for thermal calibration/equalization
  107. int calibrate(float **prow, int nrow, int offset, int width) {
  108. double teleline[MAX_HEIGHT] = { 0.0 };
  109. double wedge[16];
  110. rgparam_t regr[MAX_HEIGHT/FRAME_LEN + 1];
  111. int telestart, mtelestart = 0;
  112. int channel = -1;
  113. // The minimum rows required to decode a full frame
  114. if (nrow < 192) {
  115. fprintf(stderr, "Telemetry decoding error, not enough rows\n");
  116. return 0;
  117. }
  118. // Calculate average of a row of telemetry
  119. for (int y = 0; y < nrow; y++) {
  120. for (int x = 3; x < 43; x++)
  121. teleline[y] += prow[y][x + offset + width];
  122. teleline[y] /= 40.0;
  123. }
  124. /* Wedge 7 is white and 8 is black, this will have the largest
  125. * difference in brightness, this can be used to find the current
  126. * position within the telemetry.
  127. */
  128. float max = 0.0f;
  129. for (int n = nrow / 3 - 64; n < 2 * nrow / 3 - 64; n++) {
  130. float df;
  131. // (sum 4px below) / (sum 4px above)
  132. df = (teleline[n - 4] + teleline[n - 3] + teleline[n - 2] + teleline[n - 1]) /
  133. (teleline[n + 0] + teleline[n + 1] + teleline[n + 2] + teleline[n + 3]);
  134. // Find the maximum difference
  135. if (df > max) {
  136. mtelestart = n;
  137. max = df;
  138. }
  139. }
  140. telestart = (mtelestart - 64) % FRAME_LEN;
  141. // Make sure that theres at least one full frame in the image
  142. if (nrow < telestart + FRAME_LEN) {
  143. fprintf(stderr, "Telemetry decoding error, not enough rows\n");
  144. return 0;
  145. }
  146. // Find the least noisy frame
  147. double minNoise = -1;
  148. int bestFrame = -1;
  149. for (int n = telestart, k = 0; n < nrow - FRAME_LEN; n += FRAME_LEN, k++) {
  150. int j;
  151. for (j = 0; j < 16; j++) {
  152. int i;
  153. wedge[j] = 0.0;
  154. for (i = 1; i < 7; i++)
  155. wedge[j] += teleline[n + j * 8 + i];
  156. wedge[j] /= 6;
  157. }
  158. double noise = teleNoise(wedge);
  159. if(noise < minNoise || minNoise == -1){
  160. minNoise = noise;
  161. bestFrame = k;
  162. // Compute & apply regression on the wedges
  163. rgcomp(wedge, &regr[k]);
  164. for (int j = 0; j < 16; j++)
  165. tele[j] = rgcal(wedge[j], &regr[k]);
  166. /* Compare the channel ID wedge to the reference
  167. * wedges, the wedge with the closest match will
  168. * be the channel ID
  169. */
  170. float min = -1;
  171. for (int j = 0; j < 6; j++) {
  172. float df = tele[15] - tele[j];
  173. df *= df;
  174. if (df < min || min == -1) {
  175. channel = j;
  176. min = df;
  177. }
  178. }
  179. // Find the brightness of the minute marker, I don't really know what for
  180. Cs = 0.0;
  181. int i, j = n;
  182. for (i = 0, j = n; j < n + FRAME_LEN; j++) {
  183. float csline = 0.0;
  184. for (int l = 3; l < 43; l++)
  185. csline += prow[n][l + offset - SPC_WIDTH];
  186. csline /= 40.0;
  187. if (csline > 50.0) {
  188. Cs += csline;
  189. i++;
  190. }
  191. }
  192. Cs = rgcal(Cs / i, &regr[k]);
  193. }
  194. }
  195. if(bestFrame == -1){
  196. fprintf(stderr, "Something has gone very wrong, please file a bug report.");
  197. return 0;
  198. }
  199. calibrateImage(prow, nrow, offset, width, regr[bestFrame]);
  200. return channel + 1;
  201. }
  202. void distrib(options_t *opts, image_t *img, char *chid) {
  203. int max = 0;
  204. // Options
  205. options_t options;
  206. options.path = opts->path;
  207. options.effects = "";
  208. options.map = "";
  209. // Image options
  210. image_t distrib;
  211. strcpy(distrib.name, img->name);
  212. distrib.nrow = 256;
  213. // Assign memory
  214. for(int i = 0; i < 256; i++)
  215. distrib.prow[i] = (float *) malloc(sizeof(float) * 256);
  216. for(int n = 0; n < img->nrow; n++) {
  217. float *pixelv = img->prow[n];
  218. for(int i = 0; i < CH_WIDTH; i++) {
  219. int y = CLIP((int)pixelv[i + CHA_OFFSET], 0, 255);
  220. int x = CLIP((int)pixelv[i + CHB_OFFSET], 0, 255);
  221. distrib.prow[y][x]++;
  222. if(distrib.prow[y][x] > max)
  223. max = distrib.prow[y][x];
  224. }
  225. }
  226. // Scale to 0-255
  227. for(int x = 0; x < 256; x++)
  228. for(int y = 0; y < 256; y++)
  229. distrib.prow[y][x] = distrib.prow[y][x] / max * 255.0;
  230. extern int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, char *chid, char *palette);
  231. ImageOut(&options, &distrib, 0, 256, "Distribution", chid, NULL);
  232. }
  233. extern float quick_select(float arr[], int n);
  234. // Biased median denoise, pretyt ugly
  235. #define TRIG_LEVEL 40
  236. void denoise(float **prow, int nrow, int offset, int width){
  237. for(int y = 2; y < nrow-2; y++){
  238. for(int x = offset+1; x < offset+width-1; x++){
  239. if(prow[y][x+1] - prow[y][x] > TRIG_LEVEL ||
  240. prow[y][x-1] - prow[y][x] > TRIG_LEVEL ||
  241. prow[y+1][x] - prow[y][x] > TRIG_LEVEL ||
  242. prow[y-1][x] - prow[y][x] > TRIG_LEVEL){
  243. prow[y][x] = quick_select((float[]){
  244. prow[y+2][x-1], prow[y+2][x], prow[y+2][x+1],
  245. prow[y+1][x-1], prow[y+1][x], prow[y+1][x+1],
  246. prow[y-1][x-1], prow[y-1][x], prow[y-1][x+1],
  247. prow[y-2][x-1], prow[y-2][x], prow[y-2][x+1]
  248. }, 12);
  249. }
  250. }
  251. }
  252. }
  253. #undef TRIG_LEVEL
  254. // Flips a channe, for southbound passes
  255. void flipImage(image_t *img, int width, int offset){
  256. for(int y = 1; y < img->nrow; y++){
  257. for(int x = 1; x < ceil(width / 2.0); x++){
  258. // Flip top-left & bottom-right
  259. float buffer = img->prow[img->nrow - y][offset + x];
  260. img->prow[img->nrow - y][offset + x] = img->prow[y][offset + (width - x)];
  261. img->prow[y][offset + (width - x)] = buffer;
  262. }
  263. }
  264. }
  265. // --- Temperature Calibration --- //
  266. #include "satcal.h"
  267. typedef struct {
  268. double Nbb;
  269. double Cs;
  270. double Cb;
  271. int ch;
  272. } tempparam_t;
  273. // IR channel temperature compensation
  274. static void tempcomp(double t[16], int ch, int satnum, tempparam_t *tpr) {
  275. double Tbb, T[4];
  276. double C;
  277. tpr->ch = ch - 4;
  278. // Compute equivalent T blackbody temperature
  279. for (int n = 0; n < 4; n++) {
  280. float d0, d1, d2;
  281. C = t[9 + n] * 4.0;
  282. d0 = satcal[satnum].d[n][0];
  283. d1 = satcal[satnum].d[n][1];
  284. d2 = satcal[satnum].d[n][2];
  285. T[n] = d0;
  286. T[n] += d1 * C;
  287. C *= C;
  288. T[n] += d2 * C;
  289. }
  290. Tbb = (T[0] + T[1] + T[2] + T[3]) / 4.0;
  291. Tbb = satcal[satnum].rad[tpr->ch].A + satcal[satnum].rad[tpr->ch].B * Tbb;
  292. // Compute blackbody radiance temperature
  293. C = satcal[satnum].rad[tpr->ch].vc;
  294. tpr->Nbb = c1 * C * C * C / (expm1(c2 * C / Tbb));
  295. // Store blackbody count and space
  296. tpr->Cs = Cs * 4.0;
  297. tpr->Cb = t[14] * 4.0;
  298. }
  299. // IR channel temperature calibration
  300. static double tempcal(float Ce, int satnum, tempparam_t * rgpr) {
  301. double Nl, Nc, Ns, Ne;
  302. double T, vc;
  303. Ns = satcal[satnum].cor[rgpr->ch].Ns;
  304. Nl = Ns + (rgpr->Nbb - Ns) * (rgpr->Cs - Ce * 4.0) / (rgpr->Cs - rgpr->Cb);
  305. Nc = satcal[satnum].cor[rgpr->ch].b[0] +
  306. satcal[satnum].cor[rgpr->ch].b[1] * Nl +
  307. satcal[satnum].cor[rgpr->ch].b[2] * Nl * Nl;
  308. Ne = Nl + Nc;
  309. vc = satcal[satnum].rad[rgpr->ch].vc;
  310. T = c2 * vc / log(c1 * vc * vc * vc / Ne + 1.0);
  311. T = (T - satcal[satnum].rad[rgpr->ch].A) / satcal[satnum].rad[rgpr->ch].B;
  312. // Convert to celsius
  313. T -= 273.15;
  314. // Rescale to 0-255 for -100°C to +60°C
  315. T = (T + 100.0) / 160.0 * 255.0;
  316. return T;
  317. }
  318. // Temperature calibration wrapper
  319. void temperature(options_t *opts, image_t *img, int offset, int width){
  320. tempparam_t temp;
  321. printf("Temperature... ");
  322. fflush(stdout);
  323. tempcomp(tele, img->chB, opts->satnum - 15, &temp);
  324. for (int y = 0; y < img->nrow; y++) {
  325. for (int x = 0; x < width; x++) {
  326. img->prow[y][x + offset] = tempcal(img->prow[y][x + offset], opts->satnum - 15, &temp);
  327. }
  328. }
  329. printf("Done\n");
  330. }