summaryrefslogtreecommitdiffstats
path: root/sys/qcam/dark.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/qcam/dark.c')
-rw-r--r--sys/qcam/dark.c253
1 files changed, 253 insertions, 0 deletions
diff --git a/sys/qcam/dark.c b/sys/qcam/dark.c
new file mode 100644
index 00000000..15dbbb4e
--- /dev/null
+++ b/sys/qcam/dark.c
@@ -0,0 +1,253 @@
+/******************************************************************
+
+Copyright (C) 1996 by Brian Scearce
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+and/or distribute copies of the Software, and to permit persons to whom
+the Software is furnished to do so, subject to the following conditions:
+
+1. The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+2. Redistribution for profit requires the express, written permission of
+ the author.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL BRIAN SCEARCE BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+******************************************************************/
+
+/** Fixdark
+ Routine to repair dark current artifacts in qcam output.
+ Basic idea: the Qcam CCD suffers from "dark current";
+ that is, some of the CCD pixels will leak current under
+ long exposures, even if they're in the dark, and this
+ shows up as ugly speckling on images taken in low light.
+
+ Fortunately, the leaky pixels are the same from shot to
+ shot. So, we can figure out which pixels are leaky by
+ taking some establishing shots in the dark, and try to
+ fix those pixels on subsequent shots. The dark
+ establishing shots need only be done once per camera.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "qcam.h"
+#define MAX_LOOPS 10
+#define FNAME "qcam.darkfile"
+
+static unsigned char master_darkmask1[MAX_HEIGHT][MAX_WIDTH];
+static unsigned char master_darkmask2[MAX_HEIGHT/2+1][MAX_WIDTH/2+1];
+static unsigned char master_darkmask4[MAX_HEIGHT/4+1][MAX_WIDTH/4+1];
+
+/*
+int
+read_darkmask()
+{
+ int x, y;
+ int min_bright;
+ char darkfile[BUFSIZ], *p;
+ FILE *fp;
+
+ strcpy(darkfile, CONFIG_FILE);
+ if ( (p = strrchr(darkfile, '/'))) {
+ strcpy(p+1, FNAME);
+ } else {
+ strcpy(darkfile, FNAME);
+ }
+
+ if (!(fp = fopen(darkfile, "r"))) {
+#ifdef DEBUG
+ fprintf(stderr, "Can't open darkfile %s\n", darkfile);
+#endif
+ return 0;
+ }
+
+ if (fread(master_darkmask1, sizeof(unsigned char), MAX_WIDTH*MAX_HEIGHT, fp) !=
+ MAX_WIDTH*MAX_HEIGHT) {
+#ifdef DEBUG
+ fprintf(stderr, "Error reading darkfile\n");
+#endif
+ return 0;
+ }
+
+ for (y = 0; y < MAX_HEIGHT; y += 2) {
+ for (x = 0; x < MAX_WIDTH; x += 2) {
+ min_bright = master_darkmask1[y][x];
+ if (y < MAX_HEIGHT-1 && master_darkmask1[y+1][x] < min_bright)
+ min_bright = master_darkmask1[y+1][x];
+ if (x < MAX_WIDTH-1 && master_darkmask1[y][x+1] < min_bright)
+ min_bright = master_darkmask1[y][x+1];
+ if (y < MAX_HEIGHT-1 && x < MAX_WIDTH-1 && master_darkmask1[y+1][x+1] < min_bright)
+ min_bright = master_darkmask1[y+1][x+1];
+ master_darkmask2[y/2][x/2] = min_bright;
+ assert(y/2 < MAX_HEIGHT/2+1);
+ assert(x/2 < MAX_WIDTH/2+1);
+ }
+ }
+
+ for (y = 0; y < MAX_HEIGHT/2; y += 2) {
+ for (x = 0; x < MAX_WIDTH/2; x += 2) {
+ min_bright = master_darkmask2[y][x];
+ if (y < MAX_HEIGHT/2-1 && master_darkmask2[y+1][x] < min_bright)
+ min_bright = master_darkmask2[y+1][x];
+ if (x < MAX_WIDTH/2-1 && master_darkmask2[y][x+1] < min_bright)
+ min_bright = master_darkmask2[y][x+1];
+ if (y < MAX_HEIGHT/2-1 && x < MAX_WIDTH-1 && master_darkmask2[y+1][x+1] < min_bright)
+ min_bright = master_darkmask2[y+1][x+1];
+ master_darkmask4[y/2][x/2] = min_bright;
+ assert(y/2 < MAX_HEIGHT/4+1);
+ assert(x/2 < MAX_WIDTH/4+1);
+ }
+ }
+
+ fclose(fp);
+ return 1;
+}
+*/
+
+
+/** fixdark
+ We first record a list of bad leaky pixels, by making a
+ number of exposures in the dark. master_darkmask holds
+ this information. It's a map of the CCD.
+ master_darkmask[y][x] == val means that the pixel is
+ unreliable for brightnesses of "val" and above.
+
+ We go over the image. If a pixel is bad, look at the
+ adjacent four pixels, average the ones that have good
+ values, and use that instead.
+*/
+
+int
+fixdark(const struct qcam *q, scanbuf *scan)
+{
+ static int init = 0;
+ static int smallest_dm = 255;
+ unsigned char darkmask[MAX_HEIGHT][MAX_WIDTH];
+ unsigned char new_image[MAX_HEIGHT][MAX_WIDTH];
+ int width, height;
+ int max_width, max_height;
+ int x, y;
+ int ccd_x, ccd_y;
+ int pixelcount, pixeltotal;
+ int again, loopcount = 0;
+ int val;
+ int brightness = q->brightness;
+ int scale = q->transfer_scale;
+
+ if (!init) {
+ if (!read_darkmask()) return 0;
+ for (y = 0; y < MAX_HEIGHT; y++)
+ for (x = 0; x < MAX_HEIGHT; x++)
+ if (master_darkmask1[y][x] < smallest_dm) {
+ smallest_dm = master_darkmask1[y][x];
+#ifdef DEBUG
+ fprintf(stderr, "Smallest mask is %d at (%d, %d)\n",
+ smallest_dm, x, y);
+#endif
+ }
+ init = 1;
+ }
+
+ if (brightness < smallest_dm) {
+#ifdef DEBUG
+ fprintf(stderr, "Brightness %d (dark current starts at %d), no fixup needed\n",
+ brightness, smallest_dm);
+#endif
+ return 1;
+ }
+
+ width = q->width / scale;
+ height = q->height / scale;
+
+ max_height = MAX_HEIGHT / scale;
+ max_width = MAX_WIDTH / scale;
+ for (y = 0; y < max_height; y++)
+ for (x = 0; x < max_width; x++)
+ if (scale == 1) {
+ darkmask[y][x] = master_darkmask1[y][x];
+ } else if (scale == 2) {
+ darkmask[y][x] = master_darkmask2[y][x];
+ } else if (scale == 4) {
+ darkmask[y][x] = master_darkmask4[y][x];
+ } else {
+#ifdef DEBUG
+ fprintf(stderr, "Bad transfer_scale in darkmask assignment!\n");
+#endif
+ return 0;
+ }
+
+ do {
+ again = 0;
+ ccd_y = (q->top-1)/scale;
+ for (y = 0; y < height; y++, ccd_y++) {
+ ccd_x = q->left-1;
+ ccd_x /= 2;
+ ccd_x *= 2;
+ ccd_x /= scale;
+ for (x = 0; x < width; x++, ccd_x++) {
+ val = scan[y*width + x];
+ if (brightness < darkmask[ccd_y][ccd_x]) { /* good pixel */
+ new_image[y][x] = val;
+ } else { /* bad pixel */
+ /* look at nearby pixels, average the good values */
+ pixelcount = 0;
+ pixeltotal = 0;
+ if (x > 0) { /* left */
+ if (brightness < darkmask[ccd_y][ccd_x-1]) {
+ pixelcount++;
+ pixeltotal += scan[y*width + x - 1];
+ }
+ }
+ if (x < width-1) { /* right */
+ if (brightness < darkmask[ccd_y][ccd_x+1]) {
+ pixelcount++;
+ pixeltotal += scan[y*width + x + 1];
+ }
+ }
+ if (y > 0) { /* above */
+ if (brightness < darkmask[ccd_y-1][ccd_x]) {
+ pixelcount++;
+ pixeltotal += scan[(y-1)*width + x];
+ }
+ }
+ if (y < height-1) { /* below */
+ if (brightness < darkmask[ccd_y+1][ccd_x]) {
+ pixelcount++;
+ pixeltotal += scan[(y+1)*width + x];
+ }
+ }
+
+ if (pixelcount == 0) { /* no valid neighbors! */
+ again = 1;
+ } else {
+ new_image[y][x] = pixeltotal / pixelcount;
+ /* mark this pixel as valid, so we don't loop forever */
+ darkmask[ccd_y][ccd_x] = 255;
+ }
+ }
+ }
+ }
+
+ for (y = 0; y < height; y++)
+ for (x = 0; x < width; x++)
+ scan[y*width + x] = new_image[y][x];
+
+ } while (loopcount++ < MAX_LOOPS && again);
+#ifdef DEBUG
+ fprintf(stderr, "Darkmask fix took %d loop%s\n",
+ loopcount, (loopcount == 1)?"":"s");
+#endif
+ return 1;
+}
+