From 3182cb51e53b7a914a430a342ab8589c102eb988 Mon Sep 17 00:00:00 2001 From: Stefan Jahn Date: Fri, 11 Nov 2022 00:04:42 +0100 Subject: [PATCH 1/2] Fixed timestamps in SER files --- ser.cc | 21 ++++++++++----------- ser.h | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/ser.cc b/ser.cc index 03d4bb7..76e1217 100644 --- a/ser.cc +++ b/ser.cc @@ -188,7 +188,7 @@ int SER::writeHeader(void) { /* allocate some memory for the upcoming timestamps */ if((TimeStamps = (int64_t *)malloc(sizeof(header.DateTime) * NumberOfTimeStamps)) == NULL) { fprintf(stderr, "Error: failed to allocate %lu bytes for timestamps\n", - sizeof(header.DateTime) * NumberOfTimeStamps); + sizeof(header.DateTime) * (long)NumberOfTimeStamps); return -1; } @@ -246,13 +246,13 @@ int SER::appendFrame(void *data) { NumberOfTimeStamps *= 2; if((TimeStamps = (int64_t *)realloc(TimeStamps, sizeof(header.DateTime) * NumberOfTimeStamps)) == NULL) { fprintf(stderr, "Error: failed to re-allocate %lu bytes for timestamps\n", - sizeof(header.DateTime) * NumberOfTimeStamps); + sizeof(header.DateTime) * (long)NumberOfTimeStamps); return -1; } } /* save timestamp for this frame */ - TimeStamps[FramePointer++] = currentDateTime(); + TimeStamps[FramePointer++] = currentDateTimeUTC(); return 0; } @@ -344,7 +344,7 @@ void * SER::allocFrame(void) { /* Try allocating new frame data. */ if((Frame = malloc(FrameSize)) == NULL) { - fprintf(stderr, "Error: failed to allocate %lu bytes for frame\n", FrameSize); + fprintf(stderr, "Error: failed to allocate %lu bytes for frame\n", (long)FrameSize); return NULL; } return Frame; @@ -430,10 +430,10 @@ int32_t SER::getNumberOfFrames(void) { */ int64_t SER::setDateTime(void) { - int64_t hundred_nano_seconds = currentDateTime(); + int64_t hundred_nano_seconds = currentDateTimeUTC(); - header.DateTime = hundred_nano_seconds; - header.DateTimeUTC = hundred_nano_seconds + differenceLocalUTC(); + header.DateTimeUTC = hundred_nano_seconds; + header.DateTime = hundred_nano_seconds + differenceLocalUTC(); return header.DateTime; } @@ -448,14 +448,13 @@ int64_t SER::setDateTime(void) { maximum value represents 100 nanoseconds before the beginning of January 1 of the year 10000. */ -int64_t SER::currentDateTime(void) { +int64_t SER::currentDateTimeUTC(void) { int64_t hundred_nano_seconds; struct timeval current_time; - struct timezone current_zone; /* get time since 01.01.1970 00:00 */ - gettimeofday (¤t_time, ¤t_zone); + gettimeofday (¤t_time, NULL); hundred_nano_seconds = (62135596800L + current_time.tv_sec) * 10000000L + current_time.tv_usec * 10L; return hundred_nano_seconds; @@ -486,5 +485,5 @@ int64_t SER::differenceLocalUTC(void) { fprintf(stdout, "Debug: difference between local time and UTC is %ld hours\n", (loc_ts - gmt_ts) / 3600); - return ((int64_t)gmt_ts - (int64_t)loc_ts) * 10000000L; + return ((int64_t)loc_ts - (int64_t)gmt_ts) * 10000000L; } diff --git a/ser.h b/ser.h index d327195..cbd2628 100644 --- a/ser.h +++ b/ser.h @@ -105,7 +105,7 @@ class SER { int32_t getNumberOfFrames(void); void setNumberOfFrames(int32_t); int64_t setDateTime(void); - int64_t currentDateTime(void); + int64_t currentDateTimeUTC(void); size_t getFrameSize(void); int setFile(char *); From 9ee7aa91d8e84d24b0c568faea3a5670e102bb66 Mon Sep 17 00:00:00 2001 From: Stefan Jahn Date: Fri, 11 Nov 2022 00:06:21 +0100 Subject: [PATCH 2/2] Support for DNG file export (for RAW and RGB right now) --- ChangeLog | 3 ++ Makefile | 2 +- dng.cc | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ dng.h | 66 +++++++++++++++++++++++ main.cc | 1 + 5 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 dng.cc create mode 100644 dng.h diff --git a/ChangeLog b/ChangeLog index c2eed0e..0df678a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2022-11-10: +- support for DNG file export + 2022-11-09: - added preliminary SER file implementations, writing seems to work also with timestamps; reading not yet tested diff --git a/Makefile b/Makefile index cbf7f1c..6c33aa1 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ APP = simpleskycam -include Makefile.rules -OBJECTS := $(OBJECTS) gui.oo main.oo video.oo videoframe.oo videodev.oo videodev-v4l2.oo convert.oo filter.oo detect.oo json.oo configuration.oo ser.oo +OBJECTS := $(OBJECTS) gui.oo main.oo video.oo videoframe.oo videodev.oo videodev-v4l2.oo convert.oo filter.oo detect.oo json.oo configuration.oo ser.oo dng.oo DISTNAME=simpleskycam-$(VERSION) diff --git a/dng.cc b/dng.cc new file mode 100644 index 0000000..30c9a32 --- /dev/null +++ b/dng.cc @@ -0,0 +1,159 @@ +/* + DNG file format class based on LibTIFF. + */ + +#include +#include +#include +#include "dng.h" + +DNG::DNG() { + /* initialize public data */ + ImageWidth = 0; + ImageHeight = 0; + BitsPerSample = 0; + SamplesPerPixel = 0; + ColorID = 0; + + /* initialize internal data */ + tif = NULL; +} + +DNG::~DNG() { + /* close file if necessary */ + if(tif) { + TIFFClose(tif); + } +} + +/* + Sets the file name of the DNG file to be written. Returns -1 on + failure, 0 on success. + */ +int DNG::setFile(char * file) { + if (!(tif = TIFFOpen(file, "w"))) { + return -1; + } + return 0; +} + +/* + Sets the color ID. Only few formats are supported right now. + */ +void DNG::setColorID(int id) { + + switch(id) { + + case DNG_COLORID_RGB: + BitsPerSample = 8; + SamplesPerPixel = 3; + ColorID = id; + break; + + case DNG_COLORID_RAW16: + BitsPerSample = 16; + SamplesPerPixel = 1; + ColorID = id; + break; + + case DNG_COLORID_RAW8: + BitsPerSample = 8; + SamplesPerPixel = 1; + ColorID = id; + break; + + default: + fprintf(stderr, "Error: DNG color ID #%d not supported\n", id); + break; + } +} + +/* + Sets the image width. + */ +int DNG::setWidth(int width) { + ImageWidth = width; + return 0; +} + +/* + Gets the image width. + */ +int DNG::getWidth(void) { + return ImageWidth; +} + +/* + Sets the image height. + */ +int DNG::setHeight(int height) { + ImageHeight = height; + return 0; +} + +/* + Gets the image height. + */ +int DNG::getHeight(void) { + return ImageHeight; +} + +/* + Saves given sample data into the DNG file according to the color ID + and image size specifications. + */ +int DNG::writeFile(void * data) { + + /* create timestamp */ + time_t abs_ts; + struct tm tm; + char timestamp[64]; + + time (&abs_ts); + gmtime_r (&abs_ts, &tm); + sprintf(timestamp, "%04d:%02d:%02d %02d:%02d:%02d", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + + /* set general meta data */ + TIFFSetField(tif, TIFFTAG_MAKE, "Svbony"); + TIFFSetField(tif, TIFFTAG_MODEL, "Svbony SV305"); + TIFFSetField(tif, TIFFTAG_SOFTWARE, "SimpleSkyCam v0.0.1"); + TIFFSetField(tif, TIFFTAG_ARTIST, "Steffen Pohle"); + TIFFSetField(tif, TIFFTAG_DATETIME, timestamp); + TIFFSetField(tif, TIFFTAG_DNGVERSION, "\001\001\0\0"); + TIFFSetField(tif, TIFFTAG_DNGBACKWARDVERSION, "\001\0\0\0"); + + /* set image meta data */ + TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 0); // image + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, ImageWidth); // in pixels + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, ImageHeight); // in pixels + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, BitsPerSample); // int + TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); + TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, SamplesPerPixel); + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + + if(ColorID == DNG_COLORID_RAW8 || ColorID == DNG_COLORID_RAW8) { + static const short CFARepeatPattern[] = { 2,2 }; // 2x2 CFA + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA); + TIFFSetField(tif, TIFFTAG_CFAREPEATPATTERNDIM, CFARepeatPattern); + // 0 = Red, 1 = Green, 2 = Blue + TIFFSetField(tif, TIFFTAG_CFAPATTERN, "\001\000\002\001"); // GRGB + } + else if(ColorID == DNG_COLORID_RGB) { + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + } + + // go through all lines + for(int y = 0; y < ImageHeight; y++) { + + // calculate offset into source image based on color ID settings + int offset = y * (ImageWidth * BitsPerSample/8 * SamplesPerPixel); + + // write complete scanline + TIFFWriteScanline(tif, &((uint8_t *)data)[offset], y, 0); + } + + return 0; +} diff --git a/dng.h b/dng.h new file mode 100644 index 0000000..950e0a2 --- /dev/null +++ b/dng.h @@ -0,0 +1,66 @@ +#ifndef _DNG_H_ +#define _DNG_H_ + +/* + Example usage of the class: + --------------------------- + + DNG * dng = new DNG(); + dng->setFile((char *)"test.dng"); + dng->setWidth(640); + dng->setHeight(480); + dng->setColorID(DNG_COLORID_RGB); + uint8_t * data = (uint8_t *)malloc(dng->getWidth() * dng->getHeight() * 3); + memset(data, 0, dng->getWidth() * dng->getHeight() * 3); + for(int y = 0; y < dng->getHeight()/3; y++) { + for(int x = 0; x < dng->getWidth(); x++) { + data[y*dng->getWidth()*3 + x*3 + 0] = 0xff; + } + } + for(int y = dng->getHeight()/3; y < 2*dng->getHeight()/3; y++) { + for(int x = 0; x < dng->getWidth(); x++) { + data[y*dng->getWidth()*3 + x*3 + 1] = 0xff; + } + } + for(int y = 2*dng->getHeight()/3; y < 3*dng->getHeight()/3; y++) { + for(int x = 0; x < dng->getWidth(); x++) { + data[y*dng->getWidth()*3 + x*3 + 2] = 0xff; + } + } + dng->writeFile(data); + delete dng; +*/ + +#include + +enum { + DNG_COLORID_RGB = 100, + DNG_COLORID_RAW16 = 101, + DNG_COLORID_RAW8 = 102 +}; + +class DNG { + + private: + TIFF * tif; + int ImageWidth; + int ImageHeight; + int BitsPerSample; + int SamplesPerPixel; + int ColorID; + + public: + DNG(); + ~DNG(); + + int setWidth(int); + int setHeight(int); + int getWidth(void); + int getHeight(void); + void setColorID(int); + int setFile(char *); + int writeFile(void *); + +}; + +#endif diff --git a/main.cc b/main.cc index a2893d2..e75c0e7 100644 --- a/main.cc +++ b/main.cc @@ -13,6 +13,7 @@ #include "detect.h" #include "convert.h" #include "ser.h" +#include "dng.h" /************************************************************************** * global variables