|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- /*
- * aptdec - A lightweight FOSS (NOAA) APT decoder
- * Copyright (C) 2019-2023 Xerbo (xerbo@protonmail.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
- #include <aptdec.h>
- #include <string.h>
- #include <stdio.h>
- #include <math.h>
- #include <stdlib.h>
-
- #include "algebra.h"
- #include "util.h"
- #include "filter.h"
-
- void aptdec_equalize(aptdec_image_t *img, aptdec_region_t region) {
- // Plot histogram
- size_t histogram[256] = {0};
- for (size_t y = 0; y < img->rows; y++) {
- for (size_t x = 0; x < region.width; x++) {
- histogram[img->data[y * APTDEC_IMG_WIDTH + x + region.offset]]++;
- }
- }
-
- // Calculate cumulative frequency
- size_t sum = 0, cf[256] = {0};
- for (int i = 0; i < 256; i++) {
- sum += histogram[i];
- cf[i] = sum;
- }
-
- // Apply histogram
- int area = img->rows * region.width;
- for (size_t y = 0; y < img->rows; y++) {
- for (size_t x = 0; x < region.width; x++) {
- int k = (int)img->data[y * APTDEC_IMG_WIDTH + x + region.offset];
- img->data[y * APTDEC_IMG_WIDTH + x + region.offset] = (255.0f / area) * cf[k];
- }
- }
- }
-
- // Brightness calibrate, including telemetry
- static void image_apply_linear(uint8_t *data, int rows, int offset, int width, linear_t regr) {
- for (int y = 0; y < rows; y++) {
- for (int x = 0; x < width; x++) {
- float pv = linear_calc(data[y * APTDEC_IMG_WIDTH + x + offset], regr);
- data[y * APTDEC_IMG_WIDTH + x + offset] = clamp_int(roundf(pv), 0, 255);
- }
- }
- }
-
- void aptdec_stretch(aptdec_image_t *img, aptdec_region_t region) {
- // Plot histogram
- size_t histogram[256] = { 0 };
- for (size_t y = 0; y < img->rows; y++) {
- for (size_t x = 0; x < region.width; x++) {
- histogram[img->data[y*APTDEC_IMG_WIDTH + x + region.offset]]++;
- }
- }
-
- // Calculate cumulative frequency
- size_t sum = 0;
- size_t cf[256] = { 0 };
- for (size_t i = 0; i < 256; i++) {
- sum += histogram[i];
- cf[i] = sum;
- }
-
- // Find min/max points (1st percentile)
- int min = -1, max = -1;
- for (size_t i = 0; i < 256; i++) {
- if ((float)cf[i] / (float)sum < 0.01f && min == -1) {
- min = i;
- }
- if ((float)cf[i] / (float)sum > 0.99f && max == -1) {
- max = i;
- break;
- }
- }
-
- float a = 255.0f / (max - min);
- float b = a * -min;
- image_apply_linear(img->data, img->rows, region.offset, region.width, (linear_t){a, b});
- }
-
-
-
- // Median denoise (with deviation threshold)
- void aptdec_denoise(aptdec_image_t *img, aptdec_region_t region) {
- for (size_t y = 1; y < img->rows - 1; y++) {
- for (size_t x = 1; x < region.width - 1; x++) {
- float pixels[9] = { 0.0f };
- int pixeln = 0;
- for (int y2 = -1; y2 < 2; y2++) {
- for (int x2 = -1; x2 < 2; x2++) {
- pixels[pixeln++] = img->data[(y + y2) * APTDEC_IMG_WIDTH + (x + region.offset) + x2];
- }
- }
-
- if (standard_deviation(pixels, 9) > 15) {
- img->data[y * APTDEC_IMG_WIDTH + x + region.offset] = medianf(pixels, 9);
- }
- }
- }
- }
-
- // Flips a channel, for northbound passes
- void aptdec_flip(aptdec_image_t *img, aptdec_region_t region) {
- for (size_t y = 1; y < img->rows; y++) {
- for (int x = 1; x < ceil(region.width / 2.0f); x++) {
- // Flip top-left & bottom-right
- swap_uint8(
- &img->data[(img->rows - y) * APTDEC_IMG_WIDTH + region.offset + x],
- &img->data[y * APTDEC_IMG_WIDTH + region.offset + (region.width - x)]
- );
- }
- }
- }
-
- // Calculate crop to remove noise from the start and end of an image
- #define NOISE_THRESH 2600.0
-
- #include "filter.h"
-
- int aptdec_crop(aptdec_image_t *img) {
- const float sync_pattern[] = {-1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1,
- 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 0};
-
- float *spc_rows = calloc(img->rows, sizeof(float));
- int startCrop = 0;
- int endCrop = img->rows;
-
- for (size_t y = 0; y < img->rows; y++) {
- float temp[39] = { 0.0f };
- for (size_t i = 0; i < 39; i++) {
- temp[i] = img->data[y * APTDEC_IMG_WIDTH + i];
- }
-
- spc_rows[y] = convolve(temp, &sync_pattern[0], 39);
- }
-
- // Find ends
- for (size_t y = 0; y < img->rows - 1; y++) {
- if (spc_rows[y] > NOISE_THRESH) {
- endCrop = y;
- }
- }
- for (size_t y = img->rows; y > 0; y--) {
- if (spc_rows[y] > NOISE_THRESH) {
- startCrop = y;
- }
- }
-
- printf("Crop rows: %i -> %i\n", startCrop, endCrop);
-
- // Ignore the noisy rows at the end
- img->rows = (endCrop - startCrop);
-
- // Remove the noisy rows at start
- memmove(img->data, &img->data[startCrop * APTDEC_IMG_WIDTH], img->rows * APTDEC_IMG_WIDTH * sizeof(float));
-
- free(spc_rows);
- return startCrop;
- }
|