| 1 | #include <stdio.h>
|
|---|
| 2 | #include "pslib.h"
|
|---|
| 3 | #include "stac.h"
|
|---|
| 4 |
|
|---|
| 5 | #define SQUARE(x) ((x)*(x))
|
|---|
| 6 | #define MIN(x,y) (((x) > (y)) ? (y) : (x))
|
|---|
| 7 | #define MAX(x,y) (((x) > (y)) ? (x) : (y))
|
|---|
| 8 |
|
|---|
| 9 | #define MAXCHAR 80
|
|---|
| 10 |
|
|---|
| 11 | static int numTransforms = 0; // Number of transformations performed
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 | // Hacked the original ps_ImagePixelInterpolateBILINEAR_F32 to add variances
|
|---|
| 15 | // i.e., to square the fractions when combining.
|
|---|
| 16 | inline psF64 p_psImageErrorInterpolateBILINEAR_F32(const psImage* input,
|
|---|
| 17 | float x,
|
|---|
| 18 | float y,
|
|---|
| 19 | const psImage* mask,
|
|---|
| 20 | unsigned int maskVal,
|
|---|
| 21 | psF64 unexposedValue)
|
|---|
| 22 | {
|
|---|
| 23 | double floorX = floor((psF64)(x) - 0.5);
|
|---|
| 24 | double floorY = floor((psF64)(y) - 0.5);
|
|---|
| 25 | psF64 fracX = x - 0.5 - floorX;
|
|---|
| 26 | psF64 fracY = y - 0.5 - floorY;
|
|---|
| 27 | int intFloorX = (int) floorX;
|
|---|
| 28 | int intFloorY = (int) floorY;
|
|---|
| 29 | int lastX = input->numCols - 1;
|
|---|
| 30 | int lastY = input->numRows - 1;
|
|---|
| 31 | psF32 V00;
|
|---|
| 32 | psF32 V01;
|
|---|
| 33 | psF32 V10;
|
|---|
| 34 | psF32 V11;
|
|---|
| 35 | bool valid00 = false;
|
|---|
| 36 | bool valid01 = false;
|
|---|
| 37 | bool valid10 = false;
|
|---|
| 38 | bool valid11 = false;
|
|---|
| 39 |
|
|---|
| 40 | if (intFloorY >= 0 && intFloorY <= lastY) {
|
|---|
| 41 | if (intFloorX >= 0 && intFloorX <= lastX) {
|
|---|
| 42 | V00 = input->data.F32[intFloorY][intFloorX];
|
|---|
| 43 | valid00 = true;
|
|---|
| 44 | }
|
|---|
| 45 | if (intFloorX >= -1 && intFloorX < lastX) {
|
|---|
| 46 | V10 = input->data.F32[intFloorY][intFloorX+1];
|
|---|
| 47 | valid10 = true;
|
|---|
| 48 | }
|
|---|
| 49 | }
|
|---|
| 50 | if (intFloorY >= -1 && intFloorY < lastY) {
|
|---|
| 51 | if (intFloorX >= 0 && intFloorX <= lastX) {
|
|---|
| 52 | V01 = input->data.F32[intFloorY+1][intFloorX];
|
|---|
| 53 | valid01 = true;
|
|---|
| 54 | }
|
|---|
| 55 | if (intFloorX >= -1 && intFloorX < lastX) {
|
|---|
| 56 | V11 = input->data.F32[intFloorY+1][intFloorX+1];
|
|---|
| 57 | valid11 = true;
|
|---|
| 58 | }
|
|---|
| 59 | }
|
|---|
| 60 |
|
|---|
| 61 | /* cover likely case of all pixels being valid more efficiently */
|
|---|
| 62 | if (valid00 && valid10 && valid01 && valid11) {
|
|---|
| 63 | /* formula from the ADD */
|
|---|
| 64 | return V00*SQUARE((1.0-fracX)*(1.0-fracY)) + V10*SQUARE(fracX*(1.0-fracY)) +
|
|---|
| 65 | V01*SQUARE(fracY*(1.0-fracX)) + V11*SQUARE(fracX*fracY);
|
|---|
| 66 | }
|
|---|
| 67 |
|
|---|
| 68 | /* OK, at least one pixel is not valid - need to do it piecemeal */
|
|---|
| 69 |
|
|---|
| 70 | psF64 V0;
|
|---|
| 71 | bool valid0 = true;
|
|---|
| 72 | if (valid00 && valid10) {
|
|---|
| 73 | V0 = V00*SQUARE(1-fracX)+V10*SQUARE(fracX);
|
|---|
| 74 | } else if (valid00) {
|
|---|
| 75 | V0 = V00;
|
|---|
| 76 | } else if (valid10) {
|
|---|
| 77 | V0 = V10;
|
|---|
| 78 | } else {
|
|---|
| 79 | valid0 = false;
|
|---|
| 80 | }
|
|---|
| 81 |
|
|---|
| 82 | psF64 V1;
|
|---|
| 83 | bool valid1 = true;
|
|---|
| 84 | if (valid01 && valid11) {
|
|---|
| 85 | V1 = V01*SQUARE(1-fracX)+V11*SQUARE(fracX);
|
|---|
| 86 | } else if (valid01) {
|
|---|
| 87 | V1 = V01;
|
|---|
| 88 | } else if (valid11) {
|
|---|
| 89 | V1 = V11;
|
|---|
| 90 | } else {
|
|---|
| 91 | valid1 = false;
|
|---|
| 92 | }
|
|---|
| 93 |
|
|---|
| 94 | if (valid0 && valid1) {
|
|---|
| 95 | return ( V0*SQUARE(1-fracY) + V1*SQUARE(fracY) );
|
|---|
| 96 | } else if (valid0) {
|
|---|
| 97 | return V0;
|
|---|
| 98 | } else if (valid1) {
|
|---|
| 99 | return V1;
|
|---|
| 100 | }
|
|---|
| 101 |
|
|---|
| 102 | return unexposedValue;
|
|---|
| 103 | }
|
|---|
| 104 |
|
|---|
| 105 |
|
|---|
| 106 |
|
|---|
| 107 | bool stacTransform(psArray **outputs, // Transformed images for output
|
|---|
| 108 | psArray **outErrors, // Transformed error images for output
|
|---|
| 109 | const psArray *images, // Array of images to be transformed
|
|---|
| 110 | const psArray *maps, // Array of polynomials that do the transformation
|
|---|
| 111 | const psArray *errors, // Array of error images to be transformed
|
|---|
| 112 | const psArray *masks, // Masks of input images
|
|---|
| 113 | const psImage *region, // Region of interest for transformation
|
|---|
| 114 | const stacConfig *config // Configuration
|
|---|
| 115 | )
|
|---|
| 116 | {
|
|---|
| 117 | int nImages = images->n; // Number of images
|
|---|
| 118 | int nx = config->outnx, ny = config->outny; // Size of output images
|
|---|
| 119 | numTransforms++;
|
|---|
| 120 |
|
|---|
| 121 | // Check input sizes
|
|---|
| 122 | if (images->n != maps->n) {
|
|---|
| 123 | psError("stac.transform", "Number of maps (%d) does not match number of images (%d).\n",
|
|---|
| 124 | maps->n, images->n);
|
|---|
| 125 | return false;
|
|---|
| 126 | }
|
|---|
| 127 | if (errors && (images->n != errors->n)) {
|
|---|
| 128 | psError("stac.transform", "Number of error images (%d) does not match number of images (%d).\n",
|
|---|
| 129 | errors->n, images->n);
|
|---|
| 130 | return false;
|
|---|
| 131 | }
|
|---|
| 132 |
|
|---|
| 133 | // Allocate the output images if required, otherwise check the number
|
|---|
| 134 | if (*outputs == NULL) {
|
|---|
| 135 | *outputs = psArrayAlloc(nImages);
|
|---|
| 136 | psTrace("stac.transform", 5, "Allocating space for transformed images, %dx%d\n", nx, ny);
|
|---|
| 137 | for (int i = 0; i < nImages; i++) {
|
|---|
| 138 | (*outputs)->data[i] = psImageAlloc(nx, ny, PS_TYPE_F32);
|
|---|
| 139 | }
|
|---|
| 140 | } else if ((*outputs)->n != nImages) {
|
|---|
| 141 | psError("stac.transform", "Number of output images (%d) does not match number of input images "
|
|---|
| 142 | "(%d).\n", (*outputs)->n, nImages);
|
|---|
| 143 | return false;
|
|---|
| 144 | }
|
|---|
| 145 |
|
|---|
| 146 | // Allocate the output error images, if required, otherwise check the number
|
|---|
| 147 | if (errors && (*outErrors == NULL)) {
|
|---|
| 148 | *outErrors = psArrayAlloc(errors->n);
|
|---|
| 149 | psTrace("stac.transform", 5, "Allocating space for transformed error images, %dx%d\n", nx, ny);
|
|---|
| 150 | for (int i = 0; i < nImages; i++) {
|
|---|
| 151 | (*outErrors)->data[i] = psImageAlloc(nx, ny, PS_TYPE_F32);
|
|---|
| 152 | }
|
|---|
| 153 | } else if (errors->n != (*outErrors)->n) {
|
|---|
| 154 | psError("stac.transform", "Number of error output images (%d) does not match number of input"
|
|---|
| 155 | "images (%d).\n", (*outErrors)->n, errors->n);
|
|---|
| 156 | return false;
|
|---|
| 157 | }
|
|---|
| 158 |
|
|---|
| 159 | // Check the masks, if specified
|
|---|
| 160 | if (masks != NULL) {
|
|---|
| 161 | if (masks->n != nImages) {
|
|---|
| 162 | psError("stac.transform", "Number of masks (%d) does not match number of input images (%d).\n",
|
|---|
| 163 | masks->n, nImages);
|
|---|
| 164 | return false;
|
|---|
| 165 | }
|
|---|
| 166 | for (int i = 0; i < nImages; i++) {
|
|---|
| 167 | psImage *image = images->data[i];
|
|---|
| 168 | psImage *mask = masks->data[i];
|
|---|
| 169 | if ((mask->numRows != image->numRows) || (mask->numCols != image->numCols)) {
|
|---|
| 170 | psError ("stac.transform",
|
|---|
| 171 | "Size of input mask (%dx%d) does not match that of input image (%dx%d).\n",
|
|---|
| 172 | mask->numCols, mask->numRows, image->numCols, image->numRows);
|
|---|
| 173 | return false;
|
|---|
| 174 | }
|
|---|
| 175 | }
|
|---|
| 176 | }
|
|---|
| 177 |
|
|---|
| 178 |
|
|---|
| 179 | // Stuff for the transformations
|
|---|
| 180 | psPlane *detector = psAlloc(sizeof(psPlane)); // Coordinates on the detector
|
|---|
| 181 | psPlane *sky = psAlloc(sizeof(psPlane)); // Coordinates on the sky
|
|---|
| 182 |
|
|---|
| 183 |
|
|---|
| 184 | // Iterate over the images
|
|---|
| 185 | for (int n = 0; n < nImages; n++) {
|
|---|
| 186 | psTrace("stac.transform", 1, "Transforming image %d....\n",n);
|
|---|
| 187 |
|
|---|
| 188 | // Pull out the various stuff we're working on
|
|---|
| 189 | psImage *image = images->data[n]; // The input image
|
|---|
| 190 | psPlaneTransform *map = maps->data[n]; // The map
|
|---|
| 191 | psImage *error = errors->data[n]; // The error image
|
|---|
| 192 | psImage *outImage = (*outputs)->data[n]; // The output image
|
|---|
| 193 | psImage *outError = (*outErrors)->data[n]; // The output error image
|
|---|
| 194 |
|
|---|
| 195 | #if 0
|
|---|
| 196 | // No need for initialisation, since we iterate over the entire output image.
|
|---|
| 197 | for (int y = 0; y < ny; y++) {
|
|---|
| 198 | for (int x = 0; x < nx; x++) {
|
|---|
| 199 | outImage->data.F32[y][x] = 0.0;
|
|---|
| 200 | outError->data.F32[y][x] = 0.0;
|
|---|
| 201 | }
|
|---|
| 202 | }
|
|---|
| 203 | #endif
|
|---|
| 204 |
|
|---|
| 205 | // Mask
|
|---|
| 206 | psImage *mask = NULL;
|
|---|
| 207 | if (masks != NULL) {
|
|---|
| 208 | mask = masks->data[n];
|
|---|
| 209 | }
|
|---|
| 210 |
|
|---|
| 211 | // Iterate over the output image pixels
|
|---|
| 212 | for (int y = 0; y < ny; y++) {
|
|---|
| 213 | for (int x = 0; x < nx; x++) {
|
|---|
| 214 |
|
|---|
| 215 | // Only transform those pixels requested
|
|---|
| 216 | if (!region || (region && region->data.U8[y][x])) {
|
|---|
| 217 | // Transform!
|
|---|
| 218 | sky->x = (double)x + 0.5;
|
|---|
| 219 | sky->y = (double)y + 0.5;
|
|---|
| 220 | (void)psPlaneTransformApply(detector, map, sky);
|
|---|
| 221 |
|
|---|
| 222 | // Change PS_INTERPOLATE_BILINEAR to best available technique.
|
|---|
| 223 | outImage->data.F32[y][x] = (psF32)psImagePixelInterpolate(image, detector->x + 0.5,
|
|---|
| 224 | detector->y + 0.5, mask, 1, 0.0,
|
|---|
| 225 | PS_INTERPOLATE_BILINEAR);
|
|---|
| 226 | // Error is actually the variance
|
|---|
| 227 | outError->data.F32[y][x] = (psF32)p_psImageErrorInterpolateBILINEAR_F32(error,
|
|---|
| 228 | detector->x + 0.5,
|
|---|
| 229 | detector->y + 0.5,
|
|---|
| 230 | mask, 1, 0.0);
|
|---|
| 231 | } // Pixels of interest
|
|---|
| 232 |
|
|---|
| 233 | }
|
|---|
| 234 | } // Iterating over output pixels
|
|---|
| 235 |
|
|---|
| 236 | #ifdef TESTING
|
|---|
| 237 | // Write error image out to check
|
|---|
| 238 | char shiftfile[MAXCHAR]; // Filename of shift image
|
|---|
| 239 | char errfile[MAXCHAR]; // Filename of error image
|
|---|
| 240 | sprintf(shiftfile,"%s.shift.%d",config->inputs->data[n],numTransforms);
|
|---|
| 241 | sprintf(errfile,"%s.shifterr.%d",config->inputs->data[n],numTransforms);
|
|---|
| 242 | psTrace("stac.transform.test", 6,
|
|---|
| 243 | "Output files have size: %dx%d\n",outImage->numCols,outImage->numRows);
|
|---|
| 244 | psImageWriteSection(outImage,0,0,0,NULL,0,shiftfile);
|
|---|
| 245 | psImageWriteSection(outError,0,0,0,NULL,0,errfile);
|
|---|
| 246 | #endif
|
|---|
| 247 |
|
|---|
| 248 | } // Iterating over images
|
|---|
| 249 |
|
|---|
| 250 | // Done with transformations
|
|---|
| 251 | psFree(detector);
|
|---|
| 252 | psFree(sky);
|
|---|
| 253 |
|
|---|
| 254 | return true;
|
|---|
| 255 | }
|
|---|
| 256 |
|
|---|