25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

image.c 10 KiB

21 yıl önce
21 yıl önce
21 yıl önce
21 yıl önce
21 yıl önce
21 yıl önce
21 yıl önce
21 yıl önce
21 yıl önce
21 yıl önce
21 yıl önce
21 yıl önce
21 yıl önce
21 yıl önce
21 yıl önce
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  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[3000]; // 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. } options_t;
  43. extern void polyreg(const int m, const int n, const double x[], const double y[], double c[]);
  44. // Compute regression
  45. static void rgcomp(double x[16], rgparam_t * rgpr) {
  46. // { 0.106, 0.215, 0.324, 0.433, 0.542, 0.652, 0.78, 0.87, 0.0 }
  47. const double y[9] = { 31.07, 63.02, 94.96, 126.9, 158.86, 191.1, 228.62, 255.0, 0.0 };
  48. polyreg(REGORDER, 9, x, y, rgpr -> cf);
  49. }
  50. // Convert a value to 0-255 based off the provided regression curve
  51. static double rgcal(float x, rgparam_t *rgpr) {
  52. double y, p;
  53. int i;
  54. for (i = 0, y = 0.0, p = 1.0; i < REGORDER + 1; i++) {
  55. y += rgpr->cf[i] * p;
  56. p = p * x;
  57. }
  58. return(y);
  59. }
  60. static double tele[16];
  61. static double Cs;
  62. static int nbtele;
  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 calibrateBrightness(float **prow, int nrow, int offset, int width, int telestart, rgparam_t regr[30]){
  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 = pixelv[i + offset];
  93. // Blend between the calculated regression curves
  94. /* FIXME: this can actually make the image look *worse*
  95. * if the signal has a constant input gain.
  96. */
  97. /*int k, kof;
  98. k = (n - telestart) / FRAME_LEN;
  99. if (k >= nbtele)
  100. k = nbtele - 1;
  101. kof = (n - telestart) % FRAME_LEN;
  102. if (kof < 64) {
  103. if (k < 1) {*/
  104. pv = rgcal(pv, &(regr[4]));
  105. /*} else {
  106. pv = rgcal(pv, &(regr[k])) * (64 + kof) / FRAME_LEN +
  107. rgcal(pv, &(regr[k - 1])) * (64 - kof) / FRAME_LEN;
  108. }
  109. } else {
  110. if ((k + 1) >= nbtele) {
  111. pv = rgcal(pv, &(regr[k]));
  112. } else {
  113. pv = rgcal(pv, &(regr[k])) * (192 - kof) / FRAME_LEN +
  114. rgcal(pv, &(regr[k + 1])) * (kof - 64) / FRAME_LEN;
  115. }
  116. }
  117. */
  118. pv = CLIP(pv, 0, 255);
  119. pixelv[i + offset] = pv;
  120. }
  121. }
  122. }
  123. // Get telemetry data for thermal calibration/equalization
  124. int calibrate(float **prow, int nrow, int offset, int width) {
  125. double teleline[3000] = { 0.0 };
  126. double wedge[16];
  127. rgparam_t regr[30];
  128. int n, k;
  129. int mtelestart = 0, telestart;
  130. int channel = -1;
  131. // Calculate average of a row of telemetry
  132. for (n = 0; n < nrow; n++) {
  133. float *pixelv = prow[n];
  134. // Average the center 40px
  135. for (int i = 3; i < 43; i++) teleline[n] += pixelv[i + offset + width];
  136. teleline[n] /= 40.0;
  137. }
  138. // The minimum rows required to decode a full frame
  139. if (nrow < 192) {
  140. fprintf(stderr, ERR_TELE_ROW);
  141. return(0);
  142. }
  143. /* Wedge 7 is white and 8 is black, which will have the largest
  144. * difference in brightness, this will always be in the center of
  145. * the frame and can thus be used to find the start of the frame
  146. */
  147. double max = 0.0;
  148. for (n = nrow / 3 - 64; n < 2 * nrow / 3 - 64; n++) {
  149. float df;
  150. // (sum 4px below) / (sum 4px above)
  151. df = (teleline[n - 4] + teleline[n - 3] + teleline[n - 2] + teleline[n - 1]) /
  152. (teleline[n + 0] + teleline[n + 1] + teleline[n + 2] + teleline[n + 3]);
  153. // Find the maximum difference
  154. if (df > max) {
  155. mtelestart = n;
  156. max = df;
  157. }
  158. }
  159. // Find the start of the first frame
  160. telestart = (mtelestart - FRAME_LEN/2) % FRAME_LEN;
  161. // Make sure that theres at least one full frame in the image
  162. if (nrow < telestart + FRAME_LEN) {
  163. fprintf(stderr, ERR_TELE_ROW);
  164. return(0);
  165. }
  166. // For each frame
  167. for (n = telestart, k = 0; n < nrow - FRAME_LEN; n += FRAME_LEN, k++) {
  168. float *pixelv = prow[n];
  169. int j;
  170. // Turn each wedge into a value
  171. for (j = 0; j < 16; j++) {
  172. // Average the middle 6px
  173. wedge[j] = 0.0;
  174. for (int i = 1; i < 7; i++) wedge[j] += teleline[(j * 8) + n + i];
  175. wedge[j] /= 6;
  176. }
  177. // Compute regression on the wedges
  178. rgcomp(wedge, &(regr[k]));
  179. // Read the telemetry values from the middle of the image
  180. if (k == nrow / (2*FRAME_LEN)) {
  181. int l;
  182. // Equalise
  183. for (j = 0; j < 16; j++) tele[j] = rgcal(wedge[j], &(regr[k]));
  184. /* Compare the channel ID wedge to the reference
  185. * wedges, the wedge with the closest match will
  186. * be the channel ID
  187. */
  188. float min = -1;
  189. for (j = 0; j < 6; j++) {
  190. float df;
  191. df = tele[15] - tele[j];
  192. df *= df;
  193. if (df < min || min == -1) {
  194. channel = j;
  195. min = df;
  196. }
  197. }
  198. // Cs computation, still have no idea what this does
  199. int i;
  200. for (Cs = 0.0, i = 0, j = n; j < n + FRAME_LEN; j++) {
  201. double csline;
  202. for (csline = 0.0, l = 3; l < 43; l++)
  203. csline += pixelv[l + offset - SPC_WIDTH];
  204. csline /= 40.0;
  205. if (csline > 50.0) {
  206. Cs += csline;
  207. i++;
  208. }
  209. }
  210. Cs /= i;
  211. Cs = rgcal(Cs, &(regr[k]));
  212. }
  213. }
  214. nbtele = k;
  215. calibrateBrightness(prow, nrow, offset, width, telestart, regr);
  216. return(channel + 1);
  217. }
  218. // --- Temperature Calibration --- //
  219. #include "satcal.h"
  220. typedef struct {
  221. double Nbb;
  222. double Cs;
  223. double Cb;
  224. int ch;
  225. } tempparam_t;
  226. // IR channel temperature compensation
  227. static void tempcomp(double t[16], int ch, int satnum, tempparam_t *tpr) {
  228. double Tbb, T[4];
  229. double C;
  230. tpr -> ch = ch - 4;
  231. // Compute equivalent T blackbody temperature
  232. for (int n = 0; n < 4; n++) {
  233. float d0, d1, d2;
  234. C = t[9 + n] * 4.0;
  235. d0 = satcal[satnum].d[n][0];
  236. d1 = satcal[satnum].d[n][1];
  237. d2 = satcal[satnum].d[n][2];
  238. T[n] = d0;
  239. T[n] += d1 * C;
  240. C = C * C;
  241. T[n] += d2 * C;
  242. }
  243. Tbb = (T[0] + T[1] + T[2] + T[3]) / 4.0;
  244. Tbb = satcal[satnum].rad[tpr->ch].A + satcal[satnum].rad[tpr->ch].B * Tbb;
  245. // Compute radiance blackbody
  246. C = satcal[satnum].rad[tpr->ch].vc;
  247. tpr->Nbb = c1 * C * C * C / (expm1(c2 * C / Tbb));
  248. // Store count blackbody and space
  249. tpr->Cs = Cs * 4.0;
  250. tpr->Cb = t[14] * 4.0;
  251. }
  252. // IR channel temperature calibration
  253. static double tempcal(float Ce, int satnum, tempparam_t * rgpr) {
  254. double Nl, Nc, Ns, Ne;
  255. double T, vc;
  256. Ns = satcal[satnum].cor[rgpr->ch].Ns;
  257. Nl = Ns + (rgpr->Nbb - Ns) * (rgpr->Cs - Ce * 4.0) / (rgpr->Cs - rgpr->Cb);
  258. Nc = satcal[satnum].cor[rgpr->ch].b[0] +
  259. satcal[satnum].cor[rgpr->ch].b[1] * Nl +
  260. satcal[satnum].cor[rgpr->ch].b[2] * Nl * Nl;
  261. Ne = Nl + Nc;
  262. vc = satcal[satnum].rad[rgpr->ch].vc;
  263. T = c2 * vc / log1p(c1 * vc * vc * vc / Ne);
  264. T = (T - satcal[satnum].rad[rgpr->ch].A) / satcal[satnum].rad[rgpr->ch].B;
  265. // Rescale to 0-255 for -60'C to +40'C
  266. T = (T - 273.15 + 60.0) / 100.0 * 256.0;
  267. return(T);
  268. }
  269. // Temperature calibration wrapper
  270. void temperature(options_t *opts, image_t *img, int offset, int width){
  271. tempparam_t temp;
  272. printf("Temperature... ");
  273. fflush(stdout);
  274. tempcomp(tele, img->chB, opts->satnum - 15, &temp);
  275. for (int y = 0; y < img->nrow; y++) {
  276. float *pixelv = img->prow[y];
  277. for (int x = 0; x < width; x++) {
  278. float pv = tempcal(pixelv[x + offset], opts->satnum - 15, &temp);
  279. pixelv[x + offset] = CLIP(pv, 0, 255);
  280. }
  281. }
  282. printf("Done\n");
  283. }
  284. void distrib(options_t *opts, image_t *img, char *chid) {
  285. int max = 0;
  286. // Options
  287. options_t options;
  288. options.path = opts->path;
  289. // Image options
  290. image_t distrib;
  291. strcpy(distrib.name, img->name);
  292. distrib.nrow = 256;
  293. // Assign memory
  294. for(int i = 0; i < 256; i++)
  295. distrib.prow[i] = (float *) malloc(sizeof(float) * 256);
  296. for(int n = 0; n < img->nrow; n++) {
  297. float *pixelv = img->prow[n];
  298. for(int i = 0; i < CH_WIDTH; i++) {
  299. int y = CLIP((int)pixelv[i + CHA_OFFSET], 0, 255);
  300. int x = CLIP((int)pixelv[i + CHB_OFFSET], 0, 255);
  301. distrib.prow[y][x]++;
  302. if(distrib.prow[y][x] > max)
  303. max = distrib.prow[y][x];
  304. }
  305. }
  306. // Scale to 0-255
  307. for(int x = 0; x < 256; x++)
  308. for(int y = 0; y < 256; y++)
  309. distrib.prow[y][x] = distrib.prow[y][x] / max * 255.0;
  310. extern int ImageOut(options_t *opts, image_t *img, int offset, int width, char *desc, char *chid, char *palette);
  311. ImageOut(&options, &distrib, 0, 256, "Distribution", chid, NULL);
  312. }
  313. extern float quick_select(float arr[], int n);
  314. // Recursive biased median denoise
  315. #define TRIG_LEVEL 40
  316. void denoise(float **prow, int nrow, int offset, int width){
  317. for(int y = 2; y < nrow-2; y++){
  318. for(int x = offset+1; x < offset+width-1; x++){
  319. if(prow[y][x+1] - prow[y][x] > TRIG_LEVEL ||
  320. prow[y][x-1] - prow[y][x] > TRIG_LEVEL ||
  321. prow[y+1][x] - prow[y][x] > TRIG_LEVEL ||
  322. prow[y-1][x] - prow[y][x] > TRIG_LEVEL){
  323. prow[y][x] = quick_select((float[]){
  324. prow[y+2][x-1], prow[y+2][x], prow[y+2][x+1],
  325. prow[y+1][x-1], prow[y+1][x], prow[y+1][x+1],
  326. prow[y-1][x-1], prow[y-1][x], prow[y-1][x+1],
  327. prow[y-2][x-1], prow[y-2][x], prow[y-2][x+1]
  328. }, 12);
  329. }
  330. }
  331. }
  332. }
  333. #undef TRIG_LEVEL