From ae4b511542d8d9afa6ff116807f4e4e9a3cf9d53 Mon Sep 17 00:00:00 2001 From: eddyem Date: Sun, 19 Feb 2017 14:01:22 +0300 Subject: [PATCH] Add debayer via libraw --- Makefile | 47 ++++++++++-------- README.md => Readme.md | 0 debayer.cpp | 107 +++++++++++++++++++++++++++++++++++++++++ debayer.h | 38 +++++++++++++++ imfunctions.c | 25 +++++----- imfunctions.h | 7 +++ main.c | 1 + usefull_macros.c | 2 +- 8 files changed, 195 insertions(+), 32 deletions(-) rename README.md => Readme.md (100%) create mode 100644 debayer.cpp create mode 100644 debayer.h diff --git a/Makefile b/Makefile index 65668bc..7282641 100644 --- a/Makefile +++ b/Makefile @@ -1,37 +1,46 @@ # run `make DEF=...` to add extra defines LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all LDFLAGS += -lm -pthread +LDIMG := $(shell pkg-config --libs libraw) -lgd $(shell pkg-config --libs cfitsio) -ltiff SRCS := $(wildcard *.c) -#GCC_GE_4_9_3 := $(shell g++ -dumpversion | gawk '{print $$1>=4.9.3?"1":"0"}') -DEFINES := $(DEF) -D_XOPEN_SOURCE=1111 -#ifeq ($(GCC_GE_4_9_3),1) - DEFINES += -D_DEFAULT_SOURCE -#else -# DEFINES += -D_BSD_SOURCE -#endif +DEFINES := $(DEF) -D_GNU_SOURCE -D_XOPEN_SOURCE=1111 #DEFINES += -DEBUG -CFLAGS += -Wall -Wextra -O2 -std=gnu99 -CC = gcc +CFLAGS += -Wall -Wextra -O2 +CFLAGS += $(shell pkg-config --cflags libraw) +OBJS := $(SRCS:%.c=%.o) +CC = gcc +CPP = g++ all : sbig340 daemon client -sbig340 : $(SRCS) - @echo -e "\t\tLD sbig340" - $(CC) $(CFLAGS) $(DEFINES) $(LDFLAGS) $(shell pkg-config --libs cfitsio) -ltiff $(SRCS) -o sbig340 +debayer.o : debayer.cpp + @echo -e "\t\tG++ debayer" + $(CPP) $(CFLAGS) $(DEFINES) debayer.cpp -c + +sbig340 : $(SRCS) debayer.o + @echo -e "\t\tBuild sbig340" + $(CC) -DDAEMON $(CFLAGS) -std=gnu99 $(DEFINES) $(LDFLAGS) $(LDIMG) $(SRCS) -o $@ + +# $(CC) -c $(CFLAGS) -std=gnu99 $(DEFINES) $(SRCS) +# $(CPP) $(LDFLAGS) $(OBJS) debayer.o -o $@ daemon : $(SRCS) - @echo -e "\t\tLD daemon" - $(CC) -DDAEMON $(CFLAGS) $(DEFINES) $(LDFLAGS) $(SRCS) -o daemon + @echo -e "\t\tBuild daemon" + $(CC) -DDAEMON $(CFLAGS) -std=gnu99 $(DEFINES) $(LDFLAGS) $(SRCS) -o $@ -client : $(SRCS) - @echo -e "\t\tLD client" - $(CC) -DCLIENT $(CFLAGS) $(DEFINES) $(LDFLAGS) $(shell pkg-config --libs cfitsio) -ltiff $(SRCS) -o client +client : $(SRCS) debayer.o + @echo -e "\t\tBuild client" + $(CC) -DCLIENT $(CFLAGS) -std=gnu99 $(DEFINES) $(LDFLAGS) $(LDIMG) $(SRCS) debayer.o -o $@ clean: @echo -e "\t\tCLEAN" + @rm -f $(OBJS) debayer.o + +xclean: clean + @echo -e "\t\tRM binaries" @rm -f sbig340 daemon client gentags: - CFLAGS="$(CFLAGS) $(DEFINES)" geany -g $(PROGRAM).c.tags *[hc] 2>/dev/null + CFLAGS="$(CFLAGS) $(DEFINES)" geany -g $(PROGRAM).c.tags *[hc] *.cpp 2>/dev/null -.PHONY: gentags clean +.PHONY: gentags clean xclean diff --git a/README.md b/Readme.md similarity index 100% rename from README.md rename to Readme.md diff --git a/debayer.cpp b/debayer.cpp new file mode 100644 index 0000000..dc73dc6 --- /dev/null +++ b/debayer.cpp @@ -0,0 +1,107 @@ +/* + * geany_encoding=koi8-r + * debayer.cpp - debayer image using libraw + * based on openbayer_sample.cpp from LibRaw samples + * + * Copyright 2017 Edward V. Emelianov + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ +#include +#include +#include +#include +#include + +#include "debayer.h" +#include "usefull_macros.h" + +static int write_jpeg(const char *fname, const uint8_t *data, imstorage *img){ + if(!img) return 1; + size_t nx = img->W, ny = img->H; + gdImagePtr im = gdImageCreateTrueColor(nx, ny); + if(!im) return 4; + //for(size_t y = 0; y < ny; ++y)for(size_t x = 0; x < nx; ++x) im->tpixels[y][x] = 0XFF0000; + size_t x, y; + for(y = 0; y < ny; ++y){ + for(x = 0; x < nx; ++x){ + im->tpixels[y][x] = (data[0] << 16) | (data[1] << 8) | data[2] ; + data += 3; + } + } + FILE *fp = fopen(fname, "w"); + if(!fp){ + fprintf(stderr, "Can't save jpg image %s\n", fname); + gdImageDestroy(im); + return 5; + } + char date[256]; + strftime(date, 256, "%d/%m/%y\n%H:%M:%S", localtime(&img->exposetime)); + gdFTUseFontConfig(1); + char *ret = gdImageStringFT(im, NULL, 0xffffff, "monotype", 10, 0., 2, 12, date); + if(ret) fprintf(stderr, "Error: %s\n", ret); + im->tpixels[10][10] = 0XFF0000; + im->tpixels[15][15] = 0XFF0000; + gdImageJpeg(im, fp, 90); + fclose(fp); + gdImageDestroy(im); + return 0; +} + +/** + * Debayer image `img` and store it + * @param black - black level (minimum on image) + * @return 0 if all OK + */ +int write_debayer(imstorage *img, uint16_t black){ + char *name = make_filename(img, SUFFIX_JPEG); + if(!name) return 1; + int r = 0; + size_t fsz = img->W * img->H * sizeof(uint16_t); + LibRaw rp; + rp.imgdata.params.output_tiff = 1; + int ret = rp.open_bayer((unsigned char*)img->imdata, fsz, img->W, img->H, + 0,0,0,0,0, LIBRAW_OPENBAYER_BGGR, 0,0, black); + if(ret != LIBRAW_SUCCESS) return 2; + if ((ret = rp.unpack()) != LIBRAW_SUCCESS){ + WARNX(_("Unpack error: %d"), ret); + rp.recycle(); + return 3; + } + if((ret = rp.dcraw_process()) != LIBRAW_SUCCESS){ + WARNX(_("Processing error: %d"), ret); + rp.recycle(); + return 4; + } + libraw_processed_image_t *image = rp.dcraw_make_mem_image(&ret); + if(!image){ + WARNX(_("Can't make memory image: %d"), ret); + rp.recycle(); + return 5; + } + if(image->type != LIBRAW_IMAGE_BITMAP){ + r = 6; goto retn; + } + if(image->colors != 3){ + r = 7; goto retn; + } + write_jpeg(name, image->data, img); +retn: + LibRaw::dcraw_clear_mem(image); + rp.recycle(); + return r; +} diff --git a/debayer.h b/debayer.h new file mode 100644 index 0000000..7e350f9 --- /dev/null +++ b/debayer.h @@ -0,0 +1,38 @@ +/* + * geany_encoding=koi8-r + * debayer.h + * + * Copyright 2017 Edward V. Emelianov + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +#pragma once +#ifndef __DEBAYER_H__ +#define __DEBAYER_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "imfunctions.h" +int write_debayer(imstorage *img, uint16_t black); + +#ifdef __cplusplus +} +#endif +#endif // __DEBAYER_H__ diff --git a/imfunctions.c b/imfunctions.c index b6eac90..942595e 100644 --- a/imfunctions.c +++ b/imfunctions.c @@ -28,6 +28,7 @@ #include // sqrt #include // time, gmtime etc #ifndef DAEMON +#include "debayer.h" #include // save fits #include // save tiff #endif @@ -51,20 +52,16 @@ void modifytimestamp(char *filename, imstorage *img){ if(utimensat(AT_FDCWD, filename, times, 0)) WARN(_("Can't change timestamp for %s"), filename); } -// image type suffixes -#define SUFFIX_FITS "fits" -#define SUFFIX_RAW "bin" -#define SUFFIX_TIFF "tiff" - /** * NON THREAD-SAFE! * make filename for given name, suffix and storage type * @return filename or NULL if can't create it */ -static char *make_filename(imstorage *img, const char *suff){ +char *make_filename(imstorage *img, const char *suff){ struct stat filestat; static char buff[FILENAME_MAX]; store_type st = img->st; + DBG("Make filename from %s with suffix %s", img->imname, suff); char fnbuf[FILENAME_MAX], *outfile = img->imname; if(img->timestamp){ struct tm *stm = localtime(&img->exposetime); @@ -107,6 +104,7 @@ static char *make_filename(imstorage *img, const char *suff){ * Check image to store * @param filename (i) - output file name (or prefix with suffix) * @param store (i) - "overwrite" (or "rewrite"), "normal" (or NULL), "enumerate" (or "numerate") + * @param format (i) - image format (ft[rd]) */ imstorage *chk_storeimg(char *filename, char* store, char *format){ FNAME(); @@ -124,6 +122,7 @@ imstorage *chk_storeimg(char *filename, char* store, char *format){ char *nm = strdup(filename); if(!nm) ERRX("strdup"); char *pt = strrchr(nm, '.'); + DBG("input format: %s", format); image_format fbysuff = FORMAT_NONE; // check if name's suffix is filetype if(pt){ @@ -151,7 +150,7 @@ imstorage *chk_storeimg(char *filename, char* store, char *format){ if(fbysuff != FORMAT_NONE) fmt = fbysuff; DBG("fmt: %d", fmt); } - + DBG("imformat: %d", fmt); // now check all names #define FMTSZ (3) image_format formats[FMTSZ] = {FORMAT_FITS, FORMAT_TIFF, FORMAT_RAW}; @@ -278,6 +277,7 @@ int writefits(imstorage *img){ char buf[80]; fitsfile *fp; char *filename = make_filename(img, SUFFIX_FITS); + if(!filename) return 1; TRYFITS(fits_create_file, &fp, filename); TRYFITS(fits_create_img, fp, USHORT_IMG, 2, naxes); // FILE / Input file original name @@ -405,9 +405,9 @@ int save_histo(FILE *f, imstorage *img){ printf("low 2%% (%zd pixels) = %d, median (%zd pixels) = %d, up 2%% (%zd pixels) = %d\n", low2, lval, med, mval, up2, tval); double mul = 1., mulmax = 255. / tval; - if(tval <= 252){ // no overexposed pixels + if(tval <= 240){ // no overexposed pixels if(lval < 32){ // narrow histogram with overexposed black level - mul = 252. / tval; + mul = 240. / tval; }else mul = 32. / lval; }else{ if(mval > 134){ @@ -418,7 +418,7 @@ int save_histo(FILE *f, imstorage *img){ if(mul > mulmax) mul = mulmax; double E = img->exptime * mul; if(E < 5e-5) E = 5e-5; // too short exposition - else if(E > 120.) E = 120.; // no need to do expositions larger than 2 minutes + else if(E > 300.) E = 300.; // no need to do expositions larger than 5 minutes green("Recommended exposition time: %g seconds\n", E); exp_calculated = E; return 0; @@ -477,12 +477,13 @@ int store_image(imstorage *img){ if(img->imformat & FORMAT_TIFF){ // save tiff file if(writetiff(img)) status |= 1; } - if(img->imformat & FORMAT_RAW){ + if(img->imformat & FORMAT_RAW){ // save RAW dump & truncated histogram if(writedump(img)) status |= 2; } - if(img->imformat & FORMAT_FITS){ // not supported yet + if(img->imformat & FORMAT_FITS){ // save FITS if(writefits(img)) status |= 4; } + if(write_debayer(img, glob_min)) status |= 8; // and save colour image return status; } #endif diff --git a/imfunctions.h b/imfunctions.h index fd122d1..2d3d855 100644 --- a/imfunctions.h +++ b/imfunctions.h @@ -72,6 +72,13 @@ typedef struct{ extern double exp_calculated; +// image type suffixes +#define SUFFIX_FITS "fits.gz" +#define SUFFIX_RAW "bin" +#define SUFFIX_TIFF "tiff" +#define SUFFIX_JPEG "jpg" + +char *make_filename(imstorage *img, const char *suff); imstorage *chk_storeimg(char *filename, char* store, char *format); int store_image(imstorage *filename); void print_stat(imstorage *img); diff --git a/main.c b/main.c index 72decc4..eea84d4 100644 --- a/main.c +++ b/main.c @@ -80,6 +80,7 @@ int main(int argc, char **argv){ #endif // !CLIENT #ifndef DAEMON img = chk_storeimg(G->outpfname, G->imstoretype, G->imformat); + if(!img) return 1; #else img = MALLOC(imstorage, 1); // just allocate empty: all we need in daemon is exposition & binning #endif diff --git a/usefull_macros.c b/usefull_macros.c index 4f5ee6b..dcf5e36 100644 --- a/usefull_macros.c +++ b/usefull_macros.c @@ -333,7 +333,7 @@ size_t read_tty(uint8_t *buff, size_t length){ int retval; FD_ZERO(&rfds); FD_SET(comfd, &rfds); - tv.tv_sec = 0; tv.tv_usec = 500000; // wait for 500ms max + tv.tv_sec = 0; tv.tv_usec = 500000; // wait for 500ms retval = select(comfd + 1, &rfds, NULL, NULL, &tv); if (!retval) return 0; if(FD_ISSET(comfd, &rfds)){