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 9.6 KiB

21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  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 "offsets.h"
  25. #include "messages.h"
  26. #define REGORDER 3
  27. typedef struct {
  28. double cf[REGORDER + 1];
  29. } rgparam_t;
  30. typedef struct {
  31. float *prow[MAX_HEIGHT]; // Row buffers
  32. int nrow; // Number of rows
  33. int chA, chB; // ID of each channel
  34. char name[256]; // Stripped filename
  35. } image_t;
  36. typedef struct {
  37. char *type; // Output image type
  38. char *effects;
  39. int satnum; // The satellite number
  40. char *map; // Path to a map file
  41. char *path; // Output directory
  42. int realtime;
  43. } options_t;
  44. extern void polyreg(const int m, const int n, const double x[], const double y[], double c[]);
  45. // Compute regression
  46. static void rgcomp(double x[16], rgparam_t * rgpr) {
  47. // { 0.106, 0.215, 0.324, 0.433, 0.542, 0.652, 0.78, 0.87, 0.0 }
  48. const double y[9] = { 31.07, 63.02, 94.96, 126.9, 158.86, 191.1, 228.62, 255.0, 0.0 };
  49. polyreg(REGORDER, 9, x, y, rgpr -> cf);
  50. }
  51. // Convert a value to 0-255 based off the provided regression curve
  52. static double rgcal(float x, rgparam_t *rgpr) {
  53. double y, p;
  54. int i;
  55. for (i = 0, y = 0.0, p = 1.0; i < REGORDER + 1; i++) {
  56. y += rgpr->cf[i] * p;
  57. p = p * x;
  58. }
  59. return(y);
  60. }
  61. static double tele[16];
  62. static double Cs;
  63. void histogramEqualise(float **prow, int nrow, int offset, int width){
  64. // Plot histogram
  65. int histogram[256] = { 0 };
  66. for(int y = 0; y < nrow; y++)
  67. for(int x = 0; x < width; x++)
  68. histogram[(int)floor(prow[y][x+offset])]++;
  69. // Find min/max points
  70. int min = -1, max = -1;
  71. for(int i = 5; i < 250; i++){
  72. if(histogram[i]/width/(nrow/255.0) > 0.2){
  73. if(min == -1)
  74. min = i;
  75. max = i;
  76. }
  77. }
  78. //printf("Column %i-%i: Min: %i, Max %i\n", offset, offset+width, min, max);
  79. // Spread values to avoid overshoot
  80. min -= 5; max += 5;
  81. // Stretch the brightness into the new range
  82. for(int y = 0; y < nrow; y++)
  83. for(int x = 0; x < width; x++)
  84. prow[y][x+offset] = CLIP((prow[y][x+offset]-min) / (max-min) * 255.0, 0, 255);
  85. }
  86. // Brightness calibrate, including telemetry
  87. void calibrateImage(float **prow, int nrow, int offset, int width, rgparam_t regr){
  88. offset -= SYNC_WIDTH+SPC_WIDTH;
  89. for (int n = 0; n < nrow; n++) {
  90. float *pixelv = prow[n];
  91. for (int i = 0; i < width+SYNC_WIDTH+SPC_WIDTH+TELE_WIDTH; i++) {
  92. float pv = rgcal(pixelv[i + offset], &regr);
  93. pixelv[i + offset] = CLIP(pv, 0, 255);
  94. }
  95. }
  96. }
  97. double teleNoise(double wedges[16]){
  98. int pattern[9] = { 31, 63, 95, 127, 159, 191, 223, 255, 0 };
  99. double noise = 0;
  100. for(int i = 0; i < 9; i++)
  101. noise += fabs(wedges[i] - (double)pattern[i]);
  102. return noise;
  103. }
  104. // Get telemetry data for thermal calibration/equalization
  105. int calibrate(float **prow, int nrow, int offset, int width) {
  106. double teleline[MAX_HEIGHT] = { 0.0 };
  107. double wedge[16];
  108. rgparam_t regr[30];
  109. int telestart, mtelestart = 0;
  110. int channel = -1;
  111. // The minimum rows required to decode a full frame
  112. if (nrow < 192) {
  113. fprintf(stderr, ERR_TELE_ROW);
  114. return 0;
  115. }
  116. // Calculate average of a row of telemetry
  117. for (int n = 0; n < nrow; n++) {
  118. float *pixelv = prow[n];
  119. // Average the center 40px
  120. for (int i = 3; i < 43; i++)
  121. teleline[n] += pixelv[i + offset + width];
  122. teleline[n] /= 40.0;
  123. }
  124. /* Wedge 7 is white and 8 is black, this will have the largest
  125. * difference in brightness, this will always be in the center of
  126. * the frame and can thus be used to find the start of the frame
  127. */
  128. double max = 0.0;
  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. // Find the start of the first frame
  141. telestart = (mtelestart - FRAME_LEN/2) % FRAME_LEN;
  142. // Make sure that theres at least one full frame in the image
  143. if (nrow < telestart + FRAME_LEN) {
  144. fprintf(stderr, ERR_TELE_ROW);
  145. return(0);
  146. }
  147. // Find the least noisy frame
  148. double minNoise = -1;
  149. int bestFrame = telestart;
  150. for (int n = telestart, k = 0; n < nrow - FRAME_LEN; n += FRAME_LEN, k++) {
  151. // Turn pixels into wedge values
  152. for (int j = 0; j < 16; j++) {
  153. wedge[j] = 0.0;
  154. // Average the middle 6px
  155. for (int i = 1; i < 7; i++)
  156. wedge[j] += teleline[(j * 8) + i + n];
  157. wedge[j] /= 6;
  158. }
  159. double noise = teleNoise(wedge);
  160. if(noise < minNoise || minNoise == -1){
  161. minNoise = noise;
  162. bestFrame = k;
  163. // Compute & apply regression on the wedges
  164. rgcomp(wedge, &regr[k]);
  165. for (int j = 0; j < 16; j++)
  166. tele[j] = rgcal(wedge[j], &regr[k]);
  167. /* Compare the channel ID wedge to the reference
  168. * wedges, the wedge with the closest match will
  169. * be the channel ID
  170. */
  171. float min = -1;
  172. for (int j = 0; j < 6; j++) {
  173. float df = tele[15] - tele[j];
  174. df *= df;
  175. if (df < min || min == -1) {
  176. channel = j;
  177. min = df;
  178. }
  179. }
  180. }
  181. }
  182. calibrateImage(prow, nrow, offset, width, regr[bestFrame]);
  183. return channel + 1;
  184. }
  185. // --- Temperature Calibration --- //
  186. #include "satcal.h"
  187. typedef struct {
  188. double Nbb;
  189. double Cs;
  190. double Cb;
  191. int ch;
  192. } tempparam_t;
  193. // IR channel temperature compensation
  194. static void tempcomp(double t[16], int ch, int satnum, tempparam_t *tpr) {
  195. double Tbb, T[4];
  196. double C;
  197. tpr -> ch = ch - 4;
  198. // Compute equivalent T blackbody temperature
  199. for (int n = 0; n < 4; n++) {
  200. float d0, d1, d2;
  201. C = t[9 + n] * 4.0;
  202. d0 = satcal[satnum].d[n][0];
  203. d1 = satcal[satnum].d[n][1];
  204. d2 = satcal[satnum].d[n][2];
  205. T[n] = d0;
  206. T[n] += d1 * C;
  207. C = C * C;
  208. T[n] += d2 * C;
  209. }
  210. Tbb = (T[0] + T[1] + T[2] + T[3]) / 4.0;
  211. Tbb = satcal[satnum].rad[tpr->ch].A + satcal[satnum].rad[tpr->ch].B * Tbb;
  212. // Compute radiance blackbody
  213. C = satcal[satnum].rad[tpr->ch].vc;
  214. tpr->Nbb = c1 * C * C * C / (expm1(c2 * C / Tbb));
  215. // Store count blackbody and space
  216. tpr->Cs = Cs * 4.0;
  217. tpr->Cb = t[14] * 4.0;
  218. }
  219. // IR channel temperature calibration
  220. static double tempcal(float Ce, int satnum, tempparam_t * rgpr) {
  221. double Nl, Nc, Ns, Ne;
  222. double T, vc;
  223. Ns = satcal[satnum].cor[rgpr->ch].Ns;
  224. Nl = Ns + (rgpr->Nbb - Ns) * (rgpr->Cs - Ce * 4.0) / (rgpr->Cs - rgpr->Cb);
  225. Nc = satcal[satnum].cor[rgpr->ch].b[0] +
  226. satcal[satnum].cor[rgpr->ch].b[1] * Nl +
  227. satcal[satnum].cor[rgpr->ch].b[2] * Nl * Nl;
  228. Ne = Nl + Nc;
  229. vc = satcal[satnum].rad[rgpr->ch].vc;
  230. T = c2 * vc / log1p(c1 * vc * vc * vc / Ne);
  231. T = (T - satcal[satnum].rad[rgpr->ch].A) / satcal[satnum].rad[rgpr->ch].B;
  232. // Rescale to 0-255 for -60'C to +40'C
  233. T = (T - 273.15 + 60.0) / 100.0 * 256.0;
  234. return(T);
  235. }
  236. // Temperature calibration wrapper
  237. void temperature(options_t *opts, image_t *img, int offset, int width){
  238. tempparam_t temp;
  239. printf("Temperature... ");
  240. fflush(stdout);
  241. tempcomp(tele, img->chB, opts->satnum - 15, &temp);
  242. for (int y = 0; y < img->nrow; y++) {
  243. float *pixelv = img->prow[y];
  244. for (int x = 0; x < width; x++) {
  245. float pv = tempcal(pixelv[x + offset], opts->satnum - 15, &temp);
  246. pixelv[x + offset] = CLIP(pv, 0, 255);
  247. }
  248. }
  249. printf("Done\n");
  250. }
  251. void distrib(options_t *opts, image_t *img, char *chid) {
  252. int max = 0;
  253. // Options
  254. options_t options;
  255. options.path = opts->path;
  256. // Image options
  257. image_t distrib;
  258. strcpy(distrib.name, img->name);
  259. distrib.nrow = 256;
  260. // Assign memory
  261. for(int i = 0; i < 256; i++)
  262. distrib.prow[i] = (float *) malloc(sizeof(float) * 256);
  263. for(int n = 0; n < img->nrow; n++) {
  264. float *pixelv = img->prow[n];
  265. for(int i = 0; i < CH_WIDTH; i++) {
  266. int y = CLIP((int)pixelv[i + CHA_OFFSET], 0, 255);
  267. int x = CLIP((int)pixelv[i + CHB_OFFSET], 0, 255);
  268. distrib.prow[y][x]++;
  269. if(distrib.prow[y][x] > max)
  270. max = distrib.prow[y][x];
  271. }
  272. }
  273. // Scale to 0-255
  274. for(int x = 0; x < 256; x++)
  275. for(int y = 0; y < 256; y++)
  276. distrib.prow[y][x] = distrib.prow[y][x] / max * 255.0;
  277. extern int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, char *chid, char *palette);
  278. ImageOut(&options, &distrib, 0, 256, "Distribution", chid, NULL);
  279. }
  280. extern float quick_select(float arr[], int n);
  281. // Recursive biased median denoise
  282. #define TRIG_LEVEL 40
  283. void denoise(float **prow, int nrow, int offset, int width){
  284. for(int y = 2; y < nrow-2; y++){
  285. for(int x = offset+1; x < offset+width-1; x++){
  286. if(prow[y][x+1] - prow[y][x] > TRIG_LEVEL ||
  287. prow[y][x-1] - prow[y][x] > TRIG_LEVEL ||
  288. prow[y+1][x] - prow[y][x] > TRIG_LEVEL ||
  289. prow[y-1][x] - prow[y][x] > TRIG_LEVEL){
  290. prow[y][x] = quick_select((float[]){
  291. prow[y+2][x-1], prow[y+2][x], prow[y+2][x+1],
  292. prow[y+1][x-1], prow[y+1][x], prow[y+1][x+1],
  293. prow[y-1][x-1], prow[y-1][x], prow[y-1][x+1],
  294. prow[y-2][x-1], prow[y-2][x], prow[y-2][x+1]
  295. }, 12);
  296. }
  297. }
  298. }
  299. }
  300. #undef TRIG_LEVEL