mirror of
https://github.com/eddyem/pointgreycam.git
synced 2025-12-06 10:45:10 +03:00
add save to FITS & hystogram equalization when displaying
This commit is contained in:
parent
a1cbbd613f
commit
3073be9d09
2
Makefile
2
Makefile
@ -2,7 +2,7 @@
|
|||||||
PROGRAM := grasshopper
|
PROGRAM := grasshopper
|
||||||
LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all
|
LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all
|
||||||
LDFLAGS += -lusefull_macros -lflycapture-c -lflycapture -L/usr/local/lib
|
LDFLAGS += -lusefull_macros -lflycapture-c -lflycapture -L/usr/local/lib
|
||||||
LDFLAGS += -lm -pthread -lglut -lGL -lX11
|
LDFLAGS += -lm -pthread -lglut -lGL -lX11 -lcfitsio
|
||||||
SRCS := $(wildcard *.c)
|
SRCS := $(wildcard *.c)
|
||||||
DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111
|
DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111
|
||||||
OBJDIR := mk
|
OBJDIR := mk
|
||||||
|
|||||||
@ -59,7 +59,7 @@ static void prbl(char *s, BOOL prop){
|
|||||||
}
|
}
|
||||||
|
|
||||||
fc2Error getproperty(fc2Context context, fc2PropertyType t){
|
fc2Error getproperty(fc2Context context, fc2PropertyType t){
|
||||||
fc2Property prop;
|
fc2Property prop = {0};
|
||||||
prop.type = t;
|
prop.type = t;
|
||||||
FC2FNW(fc2GetProperty, context, &prop);
|
FC2FNW(fc2GetProperty, context, &prop);
|
||||||
if(!prop.present) return FC2_ERROR_NOT_FOUND;
|
if(!prop.present) return FC2_ERROR_NOT_FOUND;
|
||||||
@ -75,7 +75,7 @@ fc2Error getproperty(fc2Context context, fc2PropertyType t){
|
|||||||
}
|
}
|
||||||
|
|
||||||
fc2Error getpropertyInfo(fc2Context context, fc2PropertyType t){
|
fc2Error getpropertyInfo(fc2Context context, fc2PropertyType t){
|
||||||
fc2PropertyInfo i;
|
fc2PropertyInfo i = {0};
|
||||||
i.type = t;
|
i.type = t;
|
||||||
FC2FNW(fc2GetPropertyInfo, context, &i);
|
FC2FNW(fc2GetPropertyInfo, context, &i);
|
||||||
if(!i.present) return FC2_ERROR_NOT_FOUND;
|
if(!i.present) return FC2_ERROR_NOT_FOUND;
|
||||||
@ -104,9 +104,9 @@ fc2Error getpropertyInfo(fc2Context context, fc2PropertyType t){
|
|||||||
* @return FC2_ERROR_OK if all OK
|
* @return FC2_ERROR_OK if all OK
|
||||||
*/
|
*/
|
||||||
fc2Error setfloat(fc2PropertyType t, fc2Context context, float f){
|
fc2Error setfloat(fc2PropertyType t, fc2Context context, float f){
|
||||||
fc2Property prop;
|
fc2Property prop = {0};
|
||||||
prop.type = t;
|
prop.type = t;
|
||||||
fc2PropertyInfo i;
|
fc2PropertyInfo i = {0};
|
||||||
i.type = t;
|
i.type = t;
|
||||||
FC2FNW(fc2GetProperty, context, &prop);
|
FC2FNW(fc2GetProperty, context, &prop);
|
||||||
FC2FNW(fc2GetPropertyInfo, context, &i);
|
FC2FNW(fc2GetPropertyInfo, context, &i);
|
||||||
@ -146,9 +146,9 @@ fc2Error setfloat(fc2PropertyType t, fc2Context context, float f){
|
|||||||
}
|
}
|
||||||
|
|
||||||
fc2Error propOnOff(fc2PropertyType t, fc2Context context, BOOL onOff){
|
fc2Error propOnOff(fc2PropertyType t, fc2Context context, BOOL onOff){
|
||||||
fc2Property prop;
|
fc2Property prop = {0};
|
||||||
prop.type = t;
|
prop.type = t;
|
||||||
fc2PropertyInfo i;
|
fc2PropertyInfo i = {0};
|
||||||
i.type = t;
|
i.type = t;
|
||||||
FC2FNW(fc2GetPropertyInfo, context, &i);
|
FC2FNW(fc2GetPropertyInfo, context, &i);
|
||||||
FC2FNW(fc2GetProperty, context, &prop);
|
FC2FNW(fc2GetProperty, context, &prop);
|
||||||
|
|||||||
1
events.c
1
events.c
@ -57,6 +57,7 @@ static void processKeybrd(unsigned char key, int mod, _U_ int x, _U_ int y){
|
|||||||
killwindow();
|
killwindow();
|
||||||
break;
|
break;
|
||||||
case 'c': // capture in pause mode
|
case 'c': // capture in pause mode
|
||||||
|
DBG("winevt = %d", win->winevt);
|
||||||
if(win->winevt & WINEVT_PAUSE)
|
if(win->winevt & WINEVT_PAUSE)
|
||||||
win->winevt |= WINEVT_GETIMAGE;
|
win->winevt |= WINEVT_GETIMAGE;
|
||||||
break;
|
break;
|
||||||
|
|||||||
168
grasshopper.c
168
grasshopper.c
@ -25,6 +25,7 @@
|
|||||||
#include "aux.h"
|
#include "aux.h"
|
||||||
#include "camera_functions.h"
|
#include "camera_functions.h"
|
||||||
#include "cmdlnopts.h"
|
#include "cmdlnopts.h"
|
||||||
|
#include "image_functions.h"
|
||||||
#include "imageview.h"
|
#include "imageview.h"
|
||||||
|
|
||||||
void signals(int sig){
|
void signals(int sig){
|
||||||
@ -39,156 +40,6 @@ void signals(int sig){
|
|||||||
exit(sig);
|
exit(sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int GrabImage(fc2Context context, fc2Image *convertedImage){
|
|
||||||
fc2Error error;
|
|
||||||
fc2Image rawImage;
|
|
||||||
// start capture
|
|
||||||
FC2FNE(fc2StartCapture, context);
|
|
||||||
error = fc2CreateImage(&rawImage);
|
|
||||||
if(error != FC2_ERROR_OK){
|
|
||||||
printf("Error in fc2CreateImage: %s\n", fc2ErrorToDescription(error));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
// Retrieve the image
|
|
||||||
error = fc2RetrieveBuffer(context, &rawImage);
|
|
||||||
if (error != FC2_ERROR_OK){
|
|
||||||
printf("Error in retrieveBuffer: %s\n", fc2ErrorToDescription(error));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
// Convert image to gray
|
|
||||||
windowData *win = getWin();
|
|
||||||
if(win) pthread_mutex_lock(&win->mutex);
|
|
||||||
error = fc2ConvertImageTo(FC2_PIXEL_FORMAT_MONO8, &rawImage, convertedImage);
|
|
||||||
if(win) pthread_mutex_unlock(&win->mutex);
|
|
||||||
if(error != FC2_ERROR_OK){
|
|
||||||
printf("Error in fc2ConvertImageTo: %s\n", fc2ErrorToDescription(error));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
fc2StopCapture(context);
|
|
||||||
fc2DestroyImage(&rawImage);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert gray (unsigned short) into RGB components (GLubyte)
|
|
||||||
* @argument L - gray level
|
|
||||||
* @argument rgb - rgb array (GLubyte [3])
|
|
||||||
*/
|
|
||||||
static void gray2rgb(double gray, GLubyte *rgb){
|
|
||||||
int i = gray * 4.;
|
|
||||||
double x = (gray - (double)i * .25) * 4.;
|
|
||||||
GLubyte r = 0, g = 0, b = 0;
|
|
||||||
//r = g = b = (gray < 1) ? gray * 256 : 255;
|
|
||||||
switch(i){
|
|
||||||
case 0:
|
|
||||||
g = (GLubyte)(255. * x);
|
|
||||||
b = 255;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
g = 255;
|
|
||||||
b = (GLubyte)(255. * (1. - x));
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
r = (GLubyte)(255. * x);
|
|
||||||
g = 255;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
r = 255;
|
|
||||||
g = (GLubyte)(255. * (1. - x));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
r = 255;
|
|
||||||
}
|
|
||||||
*rgb++ = r;
|
|
||||||
*rgb++ = g;
|
|
||||||
*rgb = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
// functions for converting grayscale value into colour
|
|
||||||
typedef enum{
|
|
||||||
COLORFN_LINEAR, // linear
|
|
||||||
COLORFN_LOG, // ln
|
|
||||||
COLORFN_SQRT, // sqrt
|
|
||||||
COLORFN_MAX // end of list
|
|
||||||
} colorfn_type;
|
|
||||||
|
|
||||||
static colorfn_type ft = COLORFN_LINEAR;
|
|
||||||
|
|
||||||
static double linfun(double arg){ return arg; } // bung for PREVIEW_LINEAR
|
|
||||||
static double logfun(double arg){ return log(1.+arg); } // for PREVIEW_LOG
|
|
||||||
static double (*colorfun)(double) = linfun; // default function to convert color
|
|
||||||
|
|
||||||
static void change_colorfun(colorfn_type f){
|
|
||||||
DBG("New colorfn: %d", f);
|
|
||||||
switch (f){
|
|
||||||
case COLORFN_LOG:
|
|
||||||
colorfun = logfun;
|
|
||||||
ft = COLORFN_LOG;
|
|
||||||
break;
|
|
||||||
case COLORFN_SQRT:
|
|
||||||
colorfun = sqrt;
|
|
||||||
ft = COLORFN_SQRT;
|
|
||||||
break;
|
|
||||||
default: // linear
|
|
||||||
colorfun = linfun;
|
|
||||||
ft = COLORFN_LINEAR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void roll_colorfun(){
|
|
||||||
colorfn_type t = ++ft;
|
|
||||||
if(t == COLORFN_MAX) t = COLORFN_LINEAR;
|
|
||||||
change_colorfun(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void change_displayed_image(windowData *win, fc2Image *convertedImage){
|
|
||||||
if(!win || !win->image) return;
|
|
||||||
rawimage *im = win->image;
|
|
||||||
DBG("imh=%d, imw=%d, ch=%u, cw=%u", im->h, im->w, convertedImage->rows, convertedImage->cols);
|
|
||||||
/*
|
|
||||||
if(!im->rawdata || im->h != (int)convertedImage->rows || im->w != (int)convertedImage->cols){
|
|
||||||
DBG("[re]allocate im->rawdata");
|
|
||||||
FREE(im->rawdata);
|
|
||||||
im->h = (int)convertedImage->rows;
|
|
||||||
im->w = (int)convertedImage->cols;
|
|
||||||
im->rawdata = MALLOC(GLubyte, 3 * im->h * im->w);
|
|
||||||
if(!im->rawdata) ERR("Can't allocate memory");
|
|
||||||
}
|
|
||||||
printf("image data:\n");
|
|
||||||
printf("rows=%u, cols=%u, stride=%u, datasize=%u, recds=%u\n", convertedImage->rows,
|
|
||||||
convertedImage->cols, convertedImage->stride, convertedImage->dataSize, convertedImage->receivedDataSize);
|
|
||||||
*/
|
|
||||||
pthread_mutex_lock(&win->mutex);
|
|
||||||
int x, y, w = convertedImage->cols, h = convertedImage->rows;
|
|
||||||
double avr, wd, max, min;
|
|
||||||
avr = max = min = (double)*convertedImage->pData;
|
|
||||||
for(y = 0; y < h; ++y){
|
|
||||||
unsigned char *ptr = &convertedImage->pData[y*convertedImage->stride];
|
|
||||||
for(x = 0; x < w; ++x, ++ptr){
|
|
||||||
double pix = (double) *ptr;
|
|
||||||
if(pix > max) max = pix;
|
|
||||||
if(pix < min) min = pix;
|
|
||||||
avr += pix;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
avr /= (double)(w*h);
|
|
||||||
wd = max - min;
|
|
||||||
if(wd > DBL_EPSILON) avr = (avr - min) / wd; // normal average by preview
|
|
||||||
if(avr < 0.6) wd *= avr + 0.2;
|
|
||||||
if(wd < DBL_EPSILON) wd = 1.;
|
|
||||||
DBG("stat: sz=(%dx%d) avr=%g wd=%g max=%g min=%g", w,h,avr, wd, max, min);
|
|
||||||
GLubyte *dst = im->rawdata;
|
|
||||||
for(y = 0; y < h; y++){
|
|
||||||
unsigned char *ptr = &convertedImage->pData[y*convertedImage->stride];
|
|
||||||
for(x = 0; x < w; x++, dst += 3, ++ptr){
|
|
||||||
gray2rgb(colorfun((*ptr - min) / wd), dst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
win->image->changed = 1;
|
|
||||||
pthread_mutex_unlock(&win->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void savePng(fc2Image *convertedImage, char *name){
|
static void savePng(fc2Image *convertedImage, char *name){
|
||||||
VDBG("Save the image data into %s", name);
|
VDBG("Save the image data into %s", name);
|
||||||
fc2Error error = fc2SaveImage(convertedImage, name, FC2_PNG);
|
fc2Error error = fc2SaveImage(convertedImage, name, FC2_PNG);
|
||||||
@ -203,12 +54,15 @@ static void saveImages(fc2Image *convertedImage, char *prefix){
|
|||||||
if(newname) savePng(convertedImage, newname);
|
if(newname) savePng(convertedImage, newname);
|
||||||
}
|
}
|
||||||
// and save FITS here
|
// and save FITS here
|
||||||
|
char *newname = check_filename(prefix, "fits");
|
||||||
|
if(newname && writefits(newname, convertedImage))
|
||||||
|
VDBG("FITS file saved into %s", newname);
|
||||||
}
|
}
|
||||||
|
|
||||||
// manage some menu/shortcut events
|
// manage some menu/shortcut events
|
||||||
static void winevt_manage(windowData *win, fc2Image *convertedImage){
|
static void winevt_manage(windowData *win, fc2Image *convertedImage){
|
||||||
if(win->winevt & WINEVT_SAVEIMAGE){ // save image
|
if(win->winevt & WINEVT_SAVEIMAGE){ // save image
|
||||||
DBG("Try to make screenshot");
|
VDBG("Try to make screenshot");
|
||||||
saveImages(convertedImage, "ScreenShot");
|
saveImages(convertedImage, "ScreenShot");
|
||||||
win->winevt &= ~WINEVT_SAVEIMAGE;
|
win->winevt &= ~WINEVT_SAVEIMAGE;
|
||||||
}
|
}
|
||||||
@ -288,7 +142,12 @@ int main(int argc, char **argv){
|
|||||||
if(verbose_level >= VERB_MESG && numCameras > 1) PrintCameraInfo(context, G.camno);
|
if(verbose_level >= VERB_MESG && numCameras > 1) PrintCameraInfo(context, G.camno);
|
||||||
if(isnan(G.exptime)){ // no expose time -> return
|
if(isnan(G.exptime)){ // no expose time -> return
|
||||||
printf("No exposure parameters given -> exit\n");
|
printf("No exposure parameters given -> exit\n");
|
||||||
goto destr;
|
fc2StopCapture(context);
|
||||||
|
fc2DestroyContext(context);
|
||||||
|
signals(ret);
|
||||||
|
}
|
||||||
|
if(!G.showimage && !outfprefix){ // not display image & not save it?
|
||||||
|
ERRX("You should point file name or option `display image`");
|
||||||
}
|
}
|
||||||
// turn off all shit
|
// turn off all shit
|
||||||
autoExpOff(context);
|
autoExpOff(context);
|
||||||
@ -360,6 +219,11 @@ destr:
|
|||||||
if(G.showimage){
|
if(G.showimage){
|
||||||
while((mainwin = getWin())){
|
while((mainwin = getWin())){
|
||||||
if(mainwin->killthread) break;
|
if(mainwin->killthread) break;
|
||||||
|
if(mainwin->winevt & WINEVT_GETIMAGE){
|
||||||
|
mainwin->winevt &= ~WINEVT_GETIMAGE;
|
||||||
|
if(!GrabImage(context, &convertedImage))
|
||||||
|
change_displayed_image(mainwin, &convertedImage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DBG("Close window");
|
DBG("Close window");
|
||||||
clear_GL_context();
|
clear_GL_context();
|
||||||
|
|||||||
320
image_functions.c
Normal file
320
image_functions.c
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the grasshopper project.
|
||||||
|
* Copyright 2020 Edward V. Emelianov <edward.emelianoff@gmail.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 3 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fitsio.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <usefull_macros.h>
|
||||||
|
|
||||||
|
#include "camera_functions.h"
|
||||||
|
#include "cmdlnopts.h"
|
||||||
|
#include "image_functions.h"
|
||||||
|
|
||||||
|
int GrabImage(fc2Context context, fc2Image *convertedImage){
|
||||||
|
fc2Error error;
|
||||||
|
fc2Image rawImage;
|
||||||
|
// start capture
|
||||||
|
FC2FNE(fc2StartCapture, context);
|
||||||
|
error = fc2CreateImage(&rawImage);
|
||||||
|
if(error != FC2_ERROR_OK){
|
||||||
|
printf("Error in fc2CreateImage: %s\n", fc2ErrorToDescription(error));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Retrieve the image
|
||||||
|
error = fc2RetrieveBuffer(context, &rawImage);
|
||||||
|
if (error != FC2_ERROR_OK){
|
||||||
|
printf("Error in retrieveBuffer: %s\n", fc2ErrorToDescription(error));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Convert image to gray
|
||||||
|
windowData *win = getWin();
|
||||||
|
if(win) pthread_mutex_lock(&win->mutex);
|
||||||
|
error = fc2ConvertImageTo(FC2_PIXEL_FORMAT_MONO8, &rawImage, convertedImage);
|
||||||
|
if(win) pthread_mutex_unlock(&win->mutex);
|
||||||
|
if(error != FC2_ERROR_OK){
|
||||||
|
printf("Error in fc2ConvertImageTo: %s\n", fc2ErrorToDescription(error));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fc2StopCapture(context);
|
||||||
|
fc2DestroyImage(&rawImage);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert gray (unsigned short) into RGB components (GLubyte)
|
||||||
|
* @argument L - gray level (0..1)
|
||||||
|
* @argument rgb - rgb array (GLubyte [3])
|
||||||
|
*/
|
||||||
|
void gray2rgb(double gray, GLubyte *rgb){
|
||||||
|
int i = gray * 4.;
|
||||||
|
double x = (gray - (double)i * .25) * 4.;
|
||||||
|
GLubyte r = 0, g = 0, b = 0;
|
||||||
|
//r = g = b = (gray < 1) ? gray * 256 : 255;
|
||||||
|
switch(i){
|
||||||
|
case 0:
|
||||||
|
g = (GLubyte)(255. * x);
|
||||||
|
b = 255;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
g = 255;
|
||||||
|
b = (GLubyte)(255. * (1. - x));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
r = (GLubyte)(255. * x);
|
||||||
|
g = 255;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
r = 255;
|
||||||
|
g = (GLubyte)(255. * (1. - x));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
r = 255;
|
||||||
|
}
|
||||||
|
*rgb++ = r;
|
||||||
|
*rgb++ = g;
|
||||||
|
*rgb = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static colorfn_type ft = COLORFN_LINEAR;
|
||||||
|
|
||||||
|
// all colorfun's should get argument in [0, 1] and return in [0, 1]
|
||||||
|
static double linfun(double arg){ return arg; } // bung for PREVIEW_LINEAR
|
||||||
|
static double logfun(double arg){ return log(1.+arg) / 0.6931472; } // for PREVIEW_LOG [log_2(x+1)]
|
||||||
|
static double (*colorfun)(double) = linfun; // default function to convert color
|
||||||
|
|
||||||
|
colorfn_type get_colorfun(){return ft;}
|
||||||
|
|
||||||
|
void change_colorfun(colorfn_type f){
|
||||||
|
DBG("New colorfn: %d", f);
|
||||||
|
switch (f){
|
||||||
|
case COLORFN_LOG:
|
||||||
|
colorfun = logfun;
|
||||||
|
ft = COLORFN_LOG;
|
||||||
|
break;
|
||||||
|
case COLORFN_SQRT:
|
||||||
|
colorfun = sqrt;
|
||||||
|
ft = COLORFN_SQRT;
|
||||||
|
break;
|
||||||
|
default: // linear
|
||||||
|
colorfun = linfun;
|
||||||
|
ft = COLORFN_LINEAR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cycle switch between palettes
|
||||||
|
void roll_colorfun(){
|
||||||
|
colorfn_type t = ++ft;
|
||||||
|
if(t == COLORFN_MAX) t = COLORFN_LINEAR;
|
||||||
|
change_colorfun(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief equalize - hystogram equalization
|
||||||
|
* @param ori (io) - input/output data
|
||||||
|
* @param w,h,s - image width, height and stride
|
||||||
|
* @return data allocated here
|
||||||
|
*/
|
||||||
|
static uint8_t *equalize(uint8_t *ori, int w, int h, int s){
|
||||||
|
uint8_t *retn = MALLOC(uint8_t, s*h);
|
||||||
|
|
||||||
|
double orig_hysto[256] = {0.}; // original hystogram
|
||||||
|
uint8_t eq_levls[256] = {0}; // levels to convert: newpix = eq_levls[oldpix]
|
||||||
|
for(int y = 0; y < h; ++y){
|
||||||
|
uint8_t *ptr = &ori[y * s];
|
||||||
|
for(int x = 0; x < w; ++x)
|
||||||
|
++orig_hysto[*ptr++];
|
||||||
|
}
|
||||||
|
double part = (double)(w*h - 1) / 256., N = 0.;
|
||||||
|
for(size_t i = 0; i < 256; ++i){
|
||||||
|
N += orig_hysto[i];
|
||||||
|
eq_levls[i] = (uint8_t)(N/part);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int y = 0; y < h; ++y){
|
||||||
|
uint8_t *iptr = &ori[y * s];
|
||||||
|
uint8_t *optr = &retn[y * s];
|
||||||
|
for(int x = 0; x < w; ++x){
|
||||||
|
//*optr++ = *iptr++;
|
||||||
|
*optr++ = eq_levls[*iptr++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void change_displayed_image(windowData *win, fc2Image *convertedImage){
|
||||||
|
if(!win || !win->image) return;
|
||||||
|
rawimage *im = win->image;
|
||||||
|
DBG("imh=%d, imw=%d, ch=%u, cw=%u", im->h, im->w, convertedImage->rows, convertedImage->cols);
|
||||||
|
/*
|
||||||
|
if(!im->rawdata || im->h != (int)convertedImage->rows || im->w != (int)convertedImage->cols){
|
||||||
|
DBG("[re]allocate im->rawdata");
|
||||||
|
FREE(im->rawdata);
|
||||||
|
im->h = (int)convertedImage->rows;
|
||||||
|
im->w = (int)convertedImage->cols;
|
||||||
|
im->rawdata = MALLOC(GLubyte, 3 * im->h * im->w);
|
||||||
|
if(!im->rawdata) ERR("Can't allocate memory");
|
||||||
|
}
|
||||||
|
printf("image data:\n");
|
||||||
|
printf("rows=%u, cols=%u, stride=%u, datasize=%u, recds=%u\n", convertedImage->rows,
|
||||||
|
convertedImage->cols, convertedImage->stride, convertedImage->dataSize, convertedImage->receivedDataSize);
|
||||||
|
*/
|
||||||
|
pthread_mutex_lock(&win->mutex);
|
||||||
|
int x, y, w = convertedImage->cols, h = convertedImage->rows, s = convertedImage->stride;
|
||||||
|
uint8_t *newima = equalize(convertedImage->pData, w, h, s);
|
||||||
|
/*
|
||||||
|
double avr, wd, max, min;
|
||||||
|
avr = max = min = (double)*convertedImage->pData;
|
||||||
|
for(y = 0; y < h; ++y){
|
||||||
|
unsigned char *ptr = &convertedImage->pData[y*convertedImage->stride];
|
||||||
|
for(x = 0; x < w; ++x, ++ptr){
|
||||||
|
double pix = (double) *ptr;
|
||||||
|
if(pix > max) max = pix;
|
||||||
|
if(pix < min) min = pix;
|
||||||
|
avr += pix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
avr /= (double)(w*h);
|
||||||
|
wd = max - min;
|
||||||
|
if(wd > DBL_EPSILON) avr = (avr - min) / wd; // normal average by preview
|
||||||
|
if(avr < 0.6) wd *= avr + 0.2;
|
||||||
|
if(wd < DBL_EPSILON) wd = 1.;
|
||||||
|
DBG("stat: sz=(%dx%d) avr=%g wd=%g max=%g min=%g", w,h,avr, wd, max, min);
|
||||||
|
*/
|
||||||
|
GLubyte *dst = im->rawdata;
|
||||||
|
for(y = 0; y < h; y++){
|
||||||
|
//unsigned char *ptr = &convertedImage->pData[y * s];
|
||||||
|
unsigned char *ptr = &newima[y * s];
|
||||||
|
for(x = 0; x < w; x++, dst += 3, ++ptr){
|
||||||
|
//gray2rgb(colorfun((*ptr - min) / wd), dst);
|
||||||
|
gray2rgb(colorfun(*ptr / 256.), dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FREE(newima);
|
||||||
|
win->image->changed = 1;
|
||||||
|
pthread_mutex_unlock(&win->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TRYFITS(f, ...) \
|
||||||
|
do{ int status = 0; \
|
||||||
|
f(__VA_ARGS__, &status); \
|
||||||
|
if (status){ \
|
||||||
|
fits_report_error(stderr, status); \
|
||||||
|
return 1;} \
|
||||||
|
}while(0)
|
||||||
|
#define WRITEKEY(...) \
|
||||||
|
do{ int status = 0; \
|
||||||
|
fits_write_key(__VA_ARGS__, &status); \
|
||||||
|
if(status) fits_report_error(stderr, status);\
|
||||||
|
}while(0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief writefits - save FITS-file
|
||||||
|
* @param filename - full filename of output file
|
||||||
|
* @param convertedImage - image to save
|
||||||
|
* @return 0 if all OK
|
||||||
|
*/
|
||||||
|
int writefits(char *filename, fc2Image *convertedImage){
|
||||||
|
int w = convertedImage->cols, s = convertedImage->stride, h = convertedImage->rows;
|
||||||
|
long naxes[2] = {w, h}; //, startTime;
|
||||||
|
double tmp = 0.0;
|
||||||
|
//struct tm *tm_starttime;
|
||||||
|
char buf[80];
|
||||||
|
time_t savetime = time(NULL);
|
||||||
|
fitsfile *fp;
|
||||||
|
TRYFITS(fits_create_file, &fp, filename);
|
||||||
|
TRYFITS(fits_create_img, fp, BYTE_IMG, 2, naxes);
|
||||||
|
// FILE / Input file original name
|
||||||
|
WRITEKEY(fp, TSTRING, "FILE", filename, "Input file original name");
|
||||||
|
// ORIGIN / organization responsible for the data
|
||||||
|
WRITEKEY(fp, TSTRING, "ORIGIN", "SAO RAS", "organization responsible for the data");
|
||||||
|
// OBSERVAT / Observatory name
|
||||||
|
WRITEKEY(fp, TSTRING, "OBSERVAT", "Special Astrophysical Observatory, Russia", "Observatory name");
|
||||||
|
fc2Context context;
|
||||||
|
if(FC2_ERROR_OK == fc2CreateContext(&context)){
|
||||||
|
fc2CameraInfo camInfo;
|
||||||
|
fc2Error error = fc2GetCameraInfo(context, &camInfo);
|
||||||
|
if(error == FC2_ERROR_OK){
|
||||||
|
// INSTRUME / Instrument
|
||||||
|
WRITEKEY(fp, TSTRING, "INSTRUME", camInfo.modelName, "Instrument");
|
||||||
|
// DETECTOR / detector
|
||||||
|
WRITEKEY(fp, TSTRING, "DETECTOR", camInfo.sensorInfo, "Detector model");
|
||||||
|
}
|
||||||
|
fc2DestroyContext(context);
|
||||||
|
}
|
||||||
|
double pixX, pixY = pixX = 6.45;
|
||||||
|
snprintf(buf, 80, "%g x %g", pixX, pixY);
|
||||||
|
// PXSIZE / pixel size
|
||||||
|
WRITEKEY(fp, TSTRING, "PXSIZE", buf, "Pixel size (um)");
|
||||||
|
WRITEKEY(fp, TDOUBLE, "XPIXSZ", &pixX, "Pixel Size X (um)");
|
||||||
|
WRITEKEY(fp, TDOUBLE, "YPIXSZ", &pixY, "Pixel Size Y (um)");
|
||||||
|
/*
|
||||||
|
if(G->exptime < 2.*DBL_EPSILON) sprintf(buf, "bias");
|
||||||
|
else if(G->dark) sprintf(buf, "dark");
|
||||||
|
else if(G->objtype) strncpy(buf, G->objtype, 80);
|
||||||
|
else sprintf(buf, "object");
|
||||||
|
// IMAGETYP / object, flat, dark, bias, scan, eta, neon, push
|
||||||
|
WRITEKEY(fp, TSTRING, "IMAGETYP", buf, "Image type");
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
// DATAMAX, DATAMIN / Max,min pixel value
|
||||||
|
int itmp = 0;
|
||||||
|
WRITEKEY(fp, TINT, "DATAMIN", &itmp, "Min pixel value");
|
||||||
|
//itmp = G->fast ? 255 : 65535;
|
||||||
|
itmp = 65535;
|
||||||
|
WRITEKEY(fp, TINT, "DATAMAX", &itmp, "Max pixel value");
|
||||||
|
WRITEKEY(fp, TUSHORT, "STATMAX", &max, "Max data value");
|
||||||
|
WRITEKEY(fp, TUSHORT, "STATMIN", &min, "Min data value");
|
||||||
|
WRITEKEY(fp, TDOUBLE, "STATAVR", &avr, "Average data value");
|
||||||
|
WRITEKEY(fp, TDOUBLE, "STATSTD", &std, "Std. of data value");
|
||||||
|
WRITEKEY(fp, TDOUBLE, "TEMP0", &G->temperature, "Camera temperature at exp. start (degr C)");
|
||||||
|
*/
|
||||||
|
tmp = (double)G.exptime / 1000.;
|
||||||
|
// EXPTIME / actual exposition time (sec)
|
||||||
|
WRITEKEY(fp, TDOUBLE, "EXPTIME", &tmp, "Actual exposition time (sec)");
|
||||||
|
// DATE / Creation date (YYYY-MM-DDThh:mm:ss, UTC)
|
||||||
|
strftime(buf, 80, "%Y-%m-%dT%H:%M:%S", gmtime(&savetime));
|
||||||
|
WRITEKEY(fp, TSTRING, "DATE", buf, "Creation date (YYYY-MM-DDThh:mm:ss, UTC)");
|
||||||
|
/*
|
||||||
|
startTime = (long)expStartsAt.tv_sec;
|
||||||
|
tm_starttime = localtime(&expStartsAt.tv_sec);
|
||||||
|
strftime(buf, 80, "exposition starts at %d/%m/%Y, %H:%M:%S (local)", tm_starttime);
|
||||||
|
tmp = startTime + (double)expStartsAt.tv_usec/1e6;
|
||||||
|
WRITEKEY(fp, TDOUBLE, "UNIXTIME", &tmp, buf);
|
||||||
|
strftime(buf, 80, "%Y/%m/%d", tm_starttime);
|
||||||
|
// DATE-OBS / DATE (YYYY/MM/DD) OF OBS.
|
||||||
|
WRITEKEY(fp, TSTRING, "DATE-OBS", buf, "DATE OF OBS. (YYYY/MM/DD, local)");
|
||||||
|
strftime(buf, 80, "%H:%M:%S", tm_starttime);
|
||||||
|
// START / Measurement start time (local) (hh:mm:ss)
|
||||||
|
WRITEKEY(fp, TSTRING, "START", buf, "Measurement start time (hh:mm:ss, local)");
|
||||||
|
*/
|
||||||
|
uint8_t *data = MALLOC(uint8_t, w*h);
|
||||||
|
// mirror upside down to make right image
|
||||||
|
for(int y = 0; y < h; y++){
|
||||||
|
memcpy(&data[y * w], &convertedImage->pData[(h-y-1) * s], w);
|
||||||
|
}
|
||||||
|
int status = 0;
|
||||||
|
fits_write_img(fp, TBYTE, 1, w * h, data, &status);
|
||||||
|
if(status) fits_report_error(stderr, status);
|
||||||
|
FREE(data);
|
||||||
|
TRYFITS(fits_close_file, fp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef TRYFITS
|
||||||
|
#undef WRITEKEY
|
||||||
45
image_functions.h
Normal file
45
image_functions.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the grasshopper project.
|
||||||
|
* Copyright 2020 Edward V. Emelianov <edward.emelianoff@gmail.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 3 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef IMAGE_FUNCTIONS__
|
||||||
|
#define IMAGE_FUNCTIONS__
|
||||||
|
|
||||||
|
#include <C/FlyCapture2_C.h>
|
||||||
|
#include <GL/glut.h>
|
||||||
|
#include "imageview.h"
|
||||||
|
|
||||||
|
// functions for converting grayscale value into colour
|
||||||
|
typedef enum{
|
||||||
|
COLORFN_LINEAR, // linear
|
||||||
|
COLORFN_LOG, // ln
|
||||||
|
COLORFN_SQRT, // sqrt
|
||||||
|
COLORFN_MAX // end of list
|
||||||
|
} colorfn_type;
|
||||||
|
|
||||||
|
int GrabImage(fc2Context context, fc2Image *convertedImage);
|
||||||
|
void change_displayed_image(windowData *win, fc2Image *convertedImage);
|
||||||
|
|
||||||
|
void gray2rgb(double gray, GLubyte *rgb);
|
||||||
|
colorfn_type get_colorfun();
|
||||||
|
void change_colorfun(colorfn_type f);
|
||||||
|
void roll_colorfun();
|
||||||
|
|
||||||
|
int writefits(char *filename, fc2Image *convertedImage);
|
||||||
|
|
||||||
|
#endif // IMAGE_FUNCTIONS__
|
||||||
22
imageview.c
22
imageview.c
@ -111,25 +111,28 @@ static void createWindow(){
|
|||||||
int killwindow(){
|
int killwindow(){
|
||||||
if(!win) return 0;
|
if(!win) return 0;
|
||||||
glutSetWindow(win->ID); // obviously set window (for closing from menu)
|
glutSetWindow(win->ID); // obviously set window (for closing from menu)
|
||||||
|
pthread_mutex_lock(&win->mutex);
|
||||||
if(!win->killthread){
|
if(!win->killthread){
|
||||||
pthread_mutex_lock(&win->mutex);
|
|
||||||
// say changed thread to die
|
// say changed thread to die
|
||||||
win->killthread = 1;
|
win->killthread = 1;
|
||||||
pthread_mutex_unlock(&win->mutex);
|
|
||||||
}
|
}
|
||||||
DBG("wait for changed thread");
|
DBG("wait for changed thread");
|
||||||
pthread_join(win->thread, NULL); // wait while thread dies
|
pthread_join(win->thread, NULL); // wait while thread dies
|
||||||
if(win->menu) glutDestroyMenu(win->menu);
|
if(win->menu) glutDestroyMenu(win->menu);
|
||||||
glutDestroyWindow(win->ID);
|
glutDestroyWindow(win->ID);
|
||||||
DBG("destroy texture %d", win->Tex);
|
DBG("destroy menu, wundow & texture %d", win->Tex);
|
||||||
glDeleteTextures(1, &(win->Tex));
|
glDeleteTextures(1, &(win->Tex));
|
||||||
glFinish();
|
//glFinish();
|
||||||
|
windowData *old = win;
|
||||||
|
win = NULL;
|
||||||
DBG("free(rawdata)");
|
DBG("free(rawdata)");
|
||||||
FREE(win->image->rawdata);
|
FREE(old->image->rawdata);
|
||||||
DBG("free(image)");
|
DBG("free(image)");
|
||||||
FREE(win->image);
|
FREE(old->image);
|
||||||
|
pthread_mutex_unlock(&old->mutex);
|
||||||
DBG("free(win)");
|
DBG("free(win)");
|
||||||
FREE(win);
|
FREE(old);
|
||||||
|
DBG("return");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,8 +205,8 @@ static void *Redraw(_U_ void *arg){
|
|||||||
FNAME();
|
FNAME();
|
||||||
while(1){
|
while(1){
|
||||||
if(!initialized){
|
if(!initialized){
|
||||||
DBG("!initialized");
|
DBG("!initialized -> exit thread");
|
||||||
pthread_exit(NULL);
|
return NULL;
|
||||||
}
|
}
|
||||||
if(win && win->ID > 0){
|
if(win && win->ID > 0){
|
||||||
redisplay(win->ID);
|
redisplay(win->ID);
|
||||||
@ -306,6 +309,7 @@ void clear_GL_context(){
|
|||||||
DBG("kill");
|
DBG("kill");
|
||||||
killwindow();
|
killwindow();
|
||||||
DBG("join");
|
DBG("join");
|
||||||
|
//pthread_cancel(GLUTthread);
|
||||||
pthread_join(GLUTthread, NULL); // wait while main thread exits
|
pthread_join(GLUTthread, NULL); // wait while main thread exits
|
||||||
DBG("main GL thread cancelled");
|
DBG("main GL thread cancelled");
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user