







//Ser #define SER_BLOCK_SIZE 178 #define SER_FILEID_SIZE 14 #define SER_LUID_SIZE 4 #define SER_COLORID_SIZE 4 #define SER_LITTLEENDIAN_SIZE 4 #define SER_WIDTH_SIZE 4 #define SER_HEIGHT_SIZE 4 #define SER_PIXELDPTH_SIZE 4 #define SER_FRAMES_SIZE 4 #define SER_OBSERVER_SIZE 40 #define SER_INSTRUMENT_SIZE 40 #define SER_TELESCOPE_SIZE 40 #define SER_DATETIME_SIZE 8 #define SER_DATETIMEUTC_SIZE 8 #define SER_FRAMES_OFFSET 38 #define SER_DATETIME_OFFSET 162 #define SER_DATETIMEUTC_OFFSET 170 int InitSer(int bpp, int width, int height, char *fname); int WriteSERHeader(char *wfname); int WriteSERImage(unsigned char *BinImage, int BinSize, int bpp); int CloseSER();
/*====================================================================*
* ser.c
*
* Copyright(c) 2022 TsukiTsuki. (http://tsukitsuki.tips/)
*====================================================================*/
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <sys/statvfs.h>
#include <string.h>
#include <math.h>
#include <regex.h>
#include "fits.h"
#include "ser.h"
unsigned char *SerImage;
char *SerHeader;
char sensorname2[18];
int FrameCount;
/*--------------------------------------------------------------------*
* SERファイル保存用の各データを初期化する
*--------------------------------------------------------------------*/
int InitSer(int bpp, int width, int height, char *fname)
{
GetSensorName(fname, sensorname2);
SerHeader = (char *)malloc(SER_BLOCK_SIZE);
if(SerHeader == NULL){
perror("malloc error");
exit(EXIT_FAILURE);
}
char *SHp = SerHeader;
for(int i=0; i<178; i++) {
*SHp++ = 0;
}
int offset = 0;
sprintf(SerHeader+offset,"LUCAM-RECORDER");
offset += SER_FILEID_SIZE;
offset += SER_LUID_SIZE;
*(int *)(SerHeader+offset) = 8; //RGGB
offset += SER_COLORID_SIZE;
offset += SER_LITTLEENDIAN_SIZE;
*(int *)(SerHeader+offset) = width;
offset += SER_WIDTH_SIZE;
*(int *)(SerHeader+offset) = height;
offset += SER_HEIGHT_SIZE;
*(int *)(SerHeader+offset) = bpp;
offset += SER_PIXELDPTH_SIZE;
offset += SER_FRAMES_SIZE;
sprintf(SerHeader+offset,"%-40s", "Observer");
offset += SER_OBSERVER_SIZE;
sprintf(SerHeader+offset,"%-40s", sensorname2);
offset += SER_INSTRUMENT_SIZE;
sprintf(SerHeader+offset,"%-40s", "Telescope");
offset += SER_TELESCOPE_SIZE;
offset += SER_DATETIME_SIZE;
offset += SER_DATETIMEUTC_SIZE;
return 0;
}
FILE *fpSer;
FILE *fpTS;
struct timeval tv;
/*--------------------------------------------------------------------*
* SERファイルにヘッダを書き出す
*--------------------------------------------------------------------*/
int WriteSERHeader(char *wfname)
{
FrameCount = 0;
fpSer = fopen64(wfname, "wb");
fwrite(SerHeader, sizeof(char), SER_BLOCK_SIZE, fpSer);
printf("Rec Start.\n");
// タイムスタンプを一旦テンポラリファイルに記録
fpTS = fopen("/tmp/timestamp.bin", "wb");
return 0;
}
/*--------------------------------------------------------------------*
* SERファイルに画像を書き出す
*--------------------------------------------------------------------*/
int WriteSERImage(unsigned char *BinImage, int BinSize, int bpp)
{
gettimeofday(&tv, NULL);
// EndianChange(SerImage, BinImage, BinSize, bpp);
fwrite(BinImage, sizeof(char), BinSize, fpSer);
FrameCount++;
fwrite(&tv, sizeof(tv), 1, fpTS); // 8bytes
return 0;
}
/*--------------------------------------------------------------------*
* 日付から100ナノ秒単位に変換
*--------------------------------------------------------------------*/
int64_t date2timestamp(int year, int month, int day, int hour, int minute, int second, int microsec){
int monthdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; //平年の月の日数
// グレゴリオ暦で年を日数に変換
year += 1900;
int year2day = (year -1) *365 + year/400 - year/100 + year/4;
// 年内の日数を計算
int mon2day;
for(int i=0; i < month; i++){
mon2day += monthdays[i];
}
int days = year2day + mon2day + day -1;
// 秒に換算
int64_t seconds = ((int64_t)hour *60 + (int64_t)minute) *60 + (int64_t)second;
seconds += (int64_t)days * 24 *60 *60;
// 100ナノ秒に換算
return (seconds * 1000 * 1000 + (int64_t)microsec) * 10;
}
/*--------------------------------------------------------------------*
* SERファイルに画像を終了する
*--------------------------------------------------------------------*/
int CloseSER()
{
struct tm *ts;
int64_t TimeDate;
// フレーム数を記録
fseek(fpSer, SER_FRAMES_OFFSET, SEEK_SET);
fwrite(&FrameCount, 1, sizeof(int), fpSer);
fclose(fpTS);
// タイムスタンプの記録ファイルを開く
fpTS = fopen("/tmp/timestamp.bin", "rb");
fread(&tv, sizeof(tv), 1, fpTS);
fclose(fpTS);
// ローカルタイムを記録
ts = localtime(&tv.tv_sec);
TimeDate = date2timestamp(ts->tm_year, ts->tm_mon, ts->tm_mday,
ts->tm_hour, ts->tm_min, ts->tm_sec, tv.tv_usec);
fseek(fpSer, SER_DATETIME_OFFSET, SEEK_SET);
fwrite(&TimeDate, sizeof(TimeDate), 1, fpSer);
printf("%04d-%02d-%02d %02d:%02d:%02d.%06d JST\n",ts->tm_year +1900, ts->tm_mon +1,
ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec, tv.tv_usec);
// UTCを記録
ts = gmtime(&tv.tv_sec);
TimeDate = date2timestamp(ts->tm_year, ts->tm_mon, ts->tm_mday,
ts->tm_hour, ts->tm_min, ts->tm_sec, tv.tv_usec);
fseek(fpSer, SER_DATETIMEUTC_OFFSET, SEEK_SET);
fwrite(&TimeDate, sizeof(TimeDate), 1, fpSer);
printf("%04d-%02d-%02d %02d:%02d:%02d.%06d UTC\n",ts->tm_year +1900, ts->tm_mon +1,
ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec, tv.tv_usec);
// タイムスタンプをトレーラに記録
fseek(fpSer, 0, SEEK_END);
fpTS = fopen("/tmp/timestamp.bin", "rb");
for(int i=0; itm_year, ts->tm_mon, ts->tm_mday,
ts->tm_hour, ts->tm_min, ts->tm_sec, tv.tv_usec);
fwrite(&TimeDate, sizeof(TimeDate), 1, fpSer);
}
fclose(fpTS);
fclose(fpSer);
printf("%dframes\n", FrameCount);
printf("Rec Stop.\n");
return 0;
}
TARGET := $(lastword $(subst /, ,$(abspath $(dir $(lastword $(MAKEFILE_LIST)))))) CC = g++ all: $(TARGET) $(TARGET): *.cpp $(CC) -O3 -Wall -fopenmp -o $(TARGET) *.cpp `pkg-config --cflags --libs opencv4` clean: rm $(TARGET)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <omp.h>
#include <glob.h>
#include "timestamp.h"
typedef struct {
char FileID[14];
int LuID;
int ColorID;
int LittleEndian;
int ImageWidth;
int ImageHeight;
int PixelDepthPerPlane;
int FrameCount;
char Observer[40];
char Instrument[40];
char Telescope[40];
long DateTime;
long DateTime_UTC;
} serHeader;
unsigned char *BinImage;
unsigned char *BinImage2;
//ファイル名をコピーして、拡張子を変換します。
char * ChangeExt(char *ext, char *Filename){
strcpy(Filename + strlen(Filename) - 3,ext);
return Filename;
}
void InitImage(unsigned short *dst, int width, int height){
#pragma omp parallel for
for(int j=0; j<height; j++){
#pragma omp parallel for
for(int i=0; i<width; i++){
*(dst+i+width*j) = 0;
}
}
}
//コピーもとのメモリから格納先メモリに比較明しながら保存します。
void SLImage(unsigned short *dst, unsigned short *src, int width, int height){
#pragma omp parallel for
for(int j=0; j<height; j++){
#pragma omp parallel for
for(int i=0; i<width; i++){
if(*(dst+i+width*j) < *(src+i+width*j))
*(dst+i+width*j) = *(src+i+width*j);
}
}
}
void WBImageRG(unsigned short *src, int width, int height){
#pragma omp parallel for
for(int j=0; j<height; j++){
#pragma omp parallel for
for(int i=0; i<width; i++){
if(i%2==0 && j%2==0){
*(src+i+width*j) = *(src+i+width*j)/2;
}else if(i%2==1 && j%2==1){
;
}else{
*(src+i+width*j) = *(src+i+width*j)/2;
}
}
}
}
cv::VideoWriter writer; // 動画ファイルを書き出すためのオブジェクトを宣言する
int readSer(char *path){
FILE *fpSer;
serHeader SerHeader;
char tmpc[41];
printf("%s\n",path);
fpSer = fopen64(path, "rb");
int r = fread(&SerHeader.FileID, 1, sizeof(SerHeader.FileID), fpSer);
strncpy(tmpc, SerHeader.FileID, 14);
tmpc[14]='\0';
r = fread(&SerHeader.LuID, 1, sizeof(SerHeader.LuID), fpSer);
r = fread(&SerHeader.ColorID, 1, sizeof(SerHeader.ColorID), fpSer);
r = fread(&SerHeader.LittleEndian, 1, sizeof(SerHeader.LittleEndian), fpSer);
r = fread(&SerHeader.ImageWidth, 1, sizeof(SEEK_CUR), fpSer);
r = fread(&SerHeader.ImageHeight, 1, sizeof(SerHeader.ImageHeight), fpSer);
r = fread(&SerHeader.PixelDepthPerPlane, 1, sizeof(SerHeader.PixelDepthPerPlane), fpSer);
r = fread(&SerHeader.FrameCount, 1, sizeof(SerHeader.FrameCount), fpSer);
r = fread(&SerHeader.Observer, 1, sizeof(SerHeader.Observer), fpSer);
strncpy(tmpc, SerHeader.Observer, 40);
tmpc[40]='\0';
r = fread(&SerHeader.Instrument, 1, sizeof(SerHeader.Instrument), fpSer);
strncpy(tmpc, SerHeader.Instrument, 40);
tmpc[40]='\0';
r = fread(&SerHeader.Telescope, 1, sizeof(SerHeader.Telescope), fpSer);
strncpy(tmpc, SerHeader.Telescope, 40);
tmpc[40]='\0';
int yeer, month, day, hour, minute, second, microsec;
r = fread(&SerHeader.DateTime, 1, sizeof(SerHeader.DateTime), fpSer);
timestamp2date(SerHeader.DateTime, &yeer, &month, &day, &hour, &minute, &second, µsec);
r = fread(&SerHeader.DateTime_UTC, 1, sizeof(SerHeader.DateTime_UTC), fpSer);
timestamp2date(SerHeader.DateTime_UTC, &yeer, &month, &day, &hour, &minute, &second, µsec);
unsigned long TrailerDate;
fseek(fpSer, -1*SerHeader.FrameCount * sizeof(TrailerDate), SEEK_END);
for(int i = 0; i < SerHeader.FrameCount; i++){
r = fread(&TrailerDate, 1, sizeof(TrailerDate), fpSer);
timestamp2date(TrailerDate, &yeer, &month, &day, &hour, &minute, &second, µsec);
}
fseek(fpSer, 178, SEEK_SET);
int RawSize = SerHeader.ImageWidth * SerHeader.ImageHeight * SerHeader.PixelDepthPerPlane / 8;
// unsigned char *BinImage = (unsigned char *)malloc(RawSize);
// unsigned char *BinImage2 = (unsigned char *)malloc(RawSize);
InitImage((unsigned short *)BinImage2, SerHeader.ImageWidth, SerHeader.ImageWidth);
cv::Mat rgb8BitMat(SerHeader.ImageHeight, SerHeader.ImageWidth, CV_8UC3); // RGB用バッファを用意
cv::Mat rgb8BitMat2(SerHeader.ImageHeight/2, SerHeader.ImageWidth/2, CV_8UC3); // RGB用バッファを用意
for(int i=0; i<SerHeader.FrameCount; i++){
int r = fread(BinImage, sizeof(char), RawSize, fpSer);
WBImageRG((unsigned short *)BinImage, SerHeader.ImageWidth, SerHeader.ImageWidth);
SLImage((unsigned short *)BinImage2, (unsigned short *)BinImage, SerHeader.ImageWidth, SerHeader.ImageWidth);
cv::Mat bayer16BitMat(SerHeader.ImageHeight, SerHeader.ImageWidth, CV_16UC1, BinImage2); // 16bit用バッファを用意して取り込み
// cv::Mat bayer16BitMat(SerHeader.ImageHeight, SerHeader.ImageWidth, CV_16UC1, BinImage); // 16bit用バッファを用意して取り込み
cv::Mat bayer8BitMat = bayer16BitMat.clone(); // 8bit用バッファを用意
// bayer8BitMat.convertTo(bayer8BitMat, CV_8UC1, 0.0625*4); // 8bitに変換
bayer8BitMat.convertTo(bayer8BitMat, CV_8UC1, 0.0625*2); // 8bitに変換
cv::cvtColor(bayer8BitMat, rgb8BitMat, cv::COLOR_BayerRG2RGB); // デベイヤー
cv::resize(rgb8BitMat, rgb8BitMat2, cv::Size(), 0.5, 0.5); // 縮小
cv::imshow("opencv_viewer", rgb8BitMat2); // ウィンドウに画像を表示する
if (cv::waitKey(1) == 'q') break; //qを押すと終了
}
char PngPath[256];
strcpy(PngPath, path);
cv::imwrite(ChangeExt((char *)"png",PngPath), rgb8BitMat); // PNGで保存
writer << rgb8BitMat; // 画像 image を動画ファイルへ書き出す
// free(BinImage2);
// free(BinImage);
fclose(fpSer);
return 0;
}
int main(int argc, char *argv[]){
char *dirname = argv[3];
char mp4path[256];
char globpath[256];
short width = atoi(argv[1]);
short height = atoi(argv[2]);
printf("OK\n");
sprintf(mp4path,"%s/out.mp4", dirname);
int fourcc = cv::VideoWriter::fourcc('m', 'p', '4', 'v'); // ビデオフォーマットの指定( ISO MPEG-4 / .mp4)
writer.open(mp4path, fourcc, 30, cv::Size(width, height));
sprintf(globpath,"%s/*.ser", dirname);
BinImage = (unsigned char *)malloc(width * height *2);
BinImage2 = (unsigned char *)malloc(width * height *2);
glob_t globbuf;
glob(globpath, 0, NULL, &globbuf);
for (int i = 0; i < (int)globbuf.gl_pathc; i++) {
readSer(globbuf.gl_pathv[i]);
}
writer.release();
free(BinImage2);
free(BinImage);
return 0;
}
#ifndef TIMESTAMP_H #define TIMESTAMP_H void timestamp2date(long int timestamp, int *year, int *month, int*day, int *hour, int*minute, int *second, int *microsec); #endif // TIMESTAMP_H
#include "timestamp.h"
static int monthdays[2][12] = {
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} //平年の月の日数
,{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} //うるう年の月の日数
};
void timestamp2date(long int timestamp, int *year, int *month, int*day, int *hour, int*minute, int *second, int *microsec){
// 時刻を求めます
*microsec = (int)((timestamp/10)%(1000*1000));
*second = (int)((timestamp/10/1000/1000) % 60);
*minute = (int)((timestamp/10/1000/1000/60) % 60);
*hour = (int)((timestamp/10/1000/1000/60/60) % 24);
// 年を求めます
int days = (int)(timestamp/10/1000/1000/60/60/24);
int year400 = days / (366 * (400/4 - 3) + 365 * (400 - (400/4 - 3)));
days = days % (366 * (100 - 3) + 365 * (400 - (100 - 3)));
int year100 = days / (366 * (100 - 3) + 365 * (100 - 3));
days = days % (366 * (100 - 3) + 365 * (100 - 3));
int year4 = days / (366 + 365 * 3);
days = days % (366 + 365 * 3);
int year1 = days / (366 + 365 * 3);
*year = days / 365 + 4 * year4 + 100 * year100 + 400 * year400;
// 月日を求めます
days = days % 365;
int *mp = monthdays[year1 / 3];
int i;
for(i=0; i<12; i++, mp++){
if(days - *mp < 0){
break;
}
days -= *mp;
}
*month = i + 1;
*day = days + 1;
}
:
:
int frames, frameCount;
cv::VideoWriter writer; // 動画ファイルを書き出すためのオブジェクトを宣言する
int readSer(char *path){
FILE *fpSer;
serHeader SerHeader;
char tmpc[41];
printf("%s\n",path);
fpSer = fopen64(path, "rb");
int r = fread(&SerHeader.FileID, 1, sizeof(SerHeader.FileID), fpSer);
strncpy(tmpc, SerHeader.FileID, 14);
tmpc[14]='\0';
r = fread(&SerHeader.LuID, 1, sizeof(SerHeader.LuID), fpSer);
r = fread(&SerHeader.ColorID, 1, sizeof(SerHeader.ColorID), fpSer);
r = fread(&SerHeader.LittleEndian, 1, sizeof(SerHeader.LittleEndian), fpSer);
r = fread(&SerHeader.ImageWidth, 1, sizeof(SEEK_CUR), fpSer);
r = fread(&SerHeader.ImageHeight, 1, sizeof(SerHeader.ImageHeight), fpSer);
r = fread(&SerHeader.PixelDepthPerPlane, 1, sizeof(SerHeader.PixelDepthPerPlane), fpSer);
r = fread(&SerHeader.FrameCount, 1, sizeof(SerHeader.FrameCount), fpSer);
r = fread(&SerHeader.Observer, 1, sizeof(SerHeader.Observer), fpSer);
strncpy(tmpc, SerHeader.Observer, 40);
tmpc[40]='\0';
r = fread(&SerHeader.Instrument, 1, sizeof(SerHeader.Instrument), fpSer);
strncpy(tmpc, SerHeader.Instrument, 40);
tmpc[40]='\0';
r = fread(&SerHeader.Telescope, 1, sizeof(SerHeader.Telescope), fpSer);
strncpy(tmpc, SerHeader.Telescope, 40);
tmpc[40]='\0';
int yeer, month, day, hour, minute, second, microsec;
r = fread(&SerHeader.DateTime, 1, sizeof(SerHeader.DateTime), fpSer);
timestamp2date(SerHeader.DateTime, &yeer, &month, &day, &hour, &minute, &second, µsec);
r = fread(&SerHeader.DateTime_UTC, 1, sizeof(SerHeader.DateTime_UTC), fpSer);
timestamp2date(SerHeader.DateTime_UTC, &yeer, &month, &day, &hour, &minute, &second, µsec);
unsigned long TrailerDate;
fseek(fpSer, -1*SerHeader.FrameCount * sizeof(TrailerDate), SEEK_END);
for(int i = 0; i < SerHeader.FrameCount; i++){
r = fread(&TrailerDate, 1, sizeof(TrailerDate), fpSer);
timestamp2date(TrailerDate, &yeer, &month, &day, &hour, &minute, &second, µsec);
}
fseek(fpSer, 178, SEEK_SET);
int RawSize = SerHeader.ImageWidth * SerHeader.ImageHeight * SerHeader.PixelDepthPerPlane / 8;
// unsigned char *BinImage = (unsigned char *)malloc(RawSize);
// unsigned char *BinImage2 = (unsigned char *)malloc(RawSize);
// InitImage((unsigned short *)BinImage2, SerHeader.ImageWidth, SerHeader.ImageWidth);
cv::Mat rgb8BitMat(SerHeader.ImageHeight, SerHeader.ImageWidth, CV_8UC3); // RGB用バッファを用意
cv::Mat rgb8BitMat2(SerHeader.ImageHeight/2, SerHeader.ImageWidth/2, CV_8UC3); // RGB用バッファを用意
for(int i=0; i < SerHeader.FrameCount; i++){
int r = fread(BinImage, sizeof(char), RawSize, fpSer);
if(frameCount == 0){
InitImage((unsigned short *)BinImage2, SerHeader.ImageWidth, SerHeader.ImageWidth);
}
WBImageRG((unsigned short *)BinImage, SerHeader.ImageWidth, SerHeader.ImageWidth);
SLImage((unsigned short *)BinImage2, (unsigned short *)BinImage, SerHeader.ImageWidth, SerHeader.ImageWidth);
cv::Mat bayer16BitMat(SerHeader.ImageHeight, SerHeader.ImageWidth, CV_16UC1, BinImage2); // 16bit用バッファを用意して取り込み
// cv::Mat bayer16BitMat(SerHeader.ImageHeight, SerHeader.ImageWidth, CV_16UC1, BinImage); // 16bit用バッファを用意して取り込み
cv::Mat bayer8BitMat = bayer16BitMat.clone(); // 8bit用バッファを用意
// bayer8BitMat.convertTo(bayer8BitMat, CV_8UC1, 0.0625*4); // 8bitに変換
bayer8BitMat.convertTo(bayer8BitMat, CV_8UC1, 0.0625*2); // 8bitに変換
cv::cvtColor(bayer8BitMat, rgb8BitMat, cv::COLOR_BayerRG2RGB); // デベイヤー
cv::resize(rgb8BitMat, rgb8BitMat2, cv::Size(), 0.5, 0.5); // 縮小
cv::imshow("opencv_viewer", rgb8BitMat2); // ウィンドウに画像を表示する
if (cv::waitKey(1) == 'q') break; //qを押すと終了
frameCount++;
if(frames <= frameCount){
frameCount = 0;
writer << rgb8BitMat; // 画像 image を動画ファイルへ書き出す
}
}
char PngPath[256];
strcpy(PngPath, path);
cv::imwrite(ChangeExt((char *)"png",PngPath), rgb8BitMat); // PNGで保存
// writer << rgb8BitMat; // 画像 image を動画ファイルへ書き出す
// free(BinImage2);
// free(BinImage);
fclose(fpSer);
return 0;
}
int main(int argc, char *argv[]){
char *dirname = argv[4];
char mp4path[256];
char globpath[256];
short width = atoi(argv[1]);
short height = atoi(argv[2]);
frames = atoi(argv[3]);
frameCount = 0;
sprintf(mp4path,"%s/out.mp4", dirname);
int fourcc = cv::VideoWriter::fourcc('m', 'p', '4', 'v'); // ビデオフォーマットの指定( ISO MPEG-4 / .mp4)
writer.open(mp4path, fourcc, 30, cv::Size(width, height));
sprintf(globpath,"%s/*.ser", dirname);
BinImage = (unsigned char *)malloc(width * height *2);
BinImage2 = (unsigned char *)malloc(width * height *2);
glob_t globbuf;
glob(globpath, 0, NULL, &globbuf);
for (int i = 0; i < (int)globbuf.gl_pathc; i++) {
readSer(globbuf.gl_pathv[i]);
}
writer.release();
free(BinImage2);
free(BinImage);
return 0;
}