CFITSIO

cfitsio 是處理 fits 格式影像的 C library

FITS影像是天文常用的影像,甚至可以存多維影像或是單一檔案儲存多張影像。

前面加上前置的#include<cfitsio.h>,並且使用專用的結構 fitsfile 來宣告指標,以該指標操作FITS檔案。

fitsio裡面的資料型態有兩種表示方法,一種是字串式,例如TFLOAT TSTRING TDOUBLE TLONG 等,另一種是數字表示的資料型態,以 -32 ( float) 32 (long) 64 (double) 表示。

編譯時,在後方加入引數 –lcfitsio –lm即可,例如:

[astro@astro999 ~]#gcc –o test test.c –lcfitsio –lm

主要用到的函式如下,務必注意資料型態以及一些必需要的變數,直接看可能不容易看明白:

究竟哪些是必要的?而一個標準的、建議的操作流程又是什麼?順序應該是?

因此建議配合最下面的範例程式一起看。




資料型態

1. 用字串表示的資料型態, 以 TFLOAT TSTRING TDOUBLE TLONG 等表示
2. 數字表示的資料型態,以 (-32 ; float) ( 32 ; long ) ( 64 ; double ) 表示


宣告的地方需要工作

1. fits檔案指標,以 fitsfile 宣告,例如 fitsfile *fptr;

2. int status,幾乎所有讀寫檔都要,status 須等於 0 才能持續進行,
幾乎每個 fits 的函式都需用到此參數,一旦出錯, status 就會等於某數值
使用 error report 函式來閱讀發生了什麼事情

3. int check,寫檔會用到的,須等於 1 才能持續進行。

其他還有 firstpix[2] 影像讀寫的起始座標 、naxes[2] 影像尺寸等等。


讀檔案
fits_open_file( &fptr, filename, READONLY, &status);
fits_get_img_dim( fptr, &naxis, &status);
fits_get_img_size( fptr, 3, &naxes[0], &status);




關閉檔案
fits_close_file(fptr, &status);

###. cfitsio bug. 當有 fits 打開時,
不能再用 FILE 檔案指標開其它類型的檔案
亦即 fits_open_file、fits_close_file兩個函式之間
不能有其他的 fptr=fopen();

###. cfitsio bug. 當一次fits_opem跟fits_close動作結束後,
每一個你寫在記憶體的 char 字串要小心,可能會被清空!
做了 fits 檔案的處理後,cfitsio 本來就會把輸入檔名的字串清空,
但是最近發現會清掉其他無辜的字串! (07.04.16)


建立新檔案
fits_create_file( &fptr, filename , &status );
fits_create_img( fptr, -32, 2, &naxes[0], &status);

      標準的好寫法
if(
check &&
!fits_create_file( &fptr, filename , &status ) &&
!fits_create_img( fptr, -32, 2, &naxes[0], &status )
)
{
firstpix[1]=1;
/* 其他要做的事情,例如寫入影像 */
}


Header處理函式

讀特定 "Keyword" 的內容
fits_read_card( fptr, "Keyword", Array, &status );

注意一、Array 是 char 陣列,大小必定為 80 不能改變,也就是宣告要 char Array[80];
注意二、即使有很多讀頭檔要讀,固定用單一個 Array[80] 讀出來,然後再sprintf到預定的 string 例如 timeobs, dateobs。

寫特定 "Keyword" 及其內容到 Header 裡面
fits_write_key( fptr, TSTRING, "Keyword", "...字串...", NULL, &status);
fits_write_key( fptr, TFLOAT, "Keyword", 變數, NULL, &status);
** 變數資料型態請看最上面

有些特定格式的寫入,須先寫進字串後,強制寫入,例如時間日期後面還必須有註解
fits_write_record( fptr, timeobs, &status);
timeobs[80] =>{ TIME-OBS= '%.2ld:%.2ld:%.2ld' / HH:MM:SS, UT }


讀寫像元函式
fits_read_pix( fptr, TFLOAT, firstpix, npixels, NULL, pix, NULL, &status);
** firstpix[3]={1,1,1}開始讀的座標
** npixels 讀的數量

fits_write_pix( fptr, TFLOAT, firstpix, npixels, outpix, &status); ** pix,一維陣列,元 素的數目不能小於 npixels

** outpix,存著要輸出的數值

狀態報告函式
if(status)fits_report_error( stderr, status);
** 建議呢...每次跑過有關 fits 的函式之後,都可以加上這行,這樣除錯很快 !

 

========================

參考範例如下,開啟一個二維FITS影像,將參數全部讀出,再寫到一個新的FITS影像,雖然單純的copy檔案不必這麼麻煩,但是這裡是為了展示範例。

=======================

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fitsio.h> /* 引入必要的header */
int main()
{
fitsfile *inputfptr, *outputfptr; /*宣告FITS專用指標*/
int status=0,check=1; /*必要變數,status=0,check=1 函式才會執行*/
int naxis; /*必要的變數,影像的維度*/
int i;
long naxes[2]={1,1}; /*必要的變數陣列,這是圖像維尺寸 */
long firstpix[2]={1,1}; /*必要的變數陣列,這是讀寫影像時的起始座標*/
char inputname[50], outputname[50], dateobs[80], timeobs[80], tmpstring[80];
float exptime, ccdtemp;
float *pix;
long npixels;
/* ----------------- 宣告完畢,正式工作 --------------- */
sprintf(inputname,"input.fits"); /*輸入的FITS檔名*/
/* 打開檔案 */
fits_open_file(&inputfptr, inputname, READONLY, &status);
if (status) fits_report_error(stderr, status);
/* 取得影像的基本訊息*/
fits_get_img_dim(inputfptr, &naxis, &status ); /*讀取影像的維度,應該都是二維*/
fits_get_img_size(inputfptr, naxis, &(naxes[0]), &status); /*讀取影像尺寸*/
if (status) fits_report_error(stderr, status);
/* 先檢驗維度,不對就停止 */
if ( naxis >3 ) {
printf("Error: images with > 3 dimensions are not supported\n");
check = 0;
}
/* 讀取header 資訊,輸出成長度80的字串 */
fits_read_card(inputfptr,"DATE-OBS", tmpstring, &status);
sprintf(dateobs,"%s",tmpstring);
fits_read_card(inputfptr,"TIME-OBS", tmpstring, &status);
sprintf(timeobs,"%s",tmpstring);
/* 為什麼先讀到 tmpstring再寫入 dateobs 及 timeobs? */
/* 因為若兩行 fits_read_card 寫在一起,前一次的字串會被清空 */
/* 例如 fits_read_card(inputfptr,"DATE-OBS", dateobs, &status); */
/* fits_read_card(inputfptr,"TIME-OBS", timeobs, &status); */
/* 則 dateobs 會被清空 */
if (status) fits_report_error(stderr, status);
/* 讀取header 資訊,輸出成相對應的已知資料型態,例如CCD溫度輸出成float */
fits_read_key(inputfptr, TFLOAT, "EXPTIME", &exptime, NULL, &status);
fits_read_key(inputfptr, TFLOAT, "CCDTEMP", &ccdtemp, NULL, &status);
if (status) fits_report_error(stderr, status);
/* 計算像元總數 */
npixels=naxes[0] * naxes[1];
/* 動態配置記憶體,等會像元的數值都會存入 pix */
pix=(float *) malloc( npixels * sizeof(float));
/* 讀取像元,這裡是一口氣讀完,也就是讀npixels次、從firstpix的座標開始 */
fits_read_pix(inputfptr, TFLOAT, firstpix, npixels, NULL, pix, NULL, &status);
if (status) fits_report_error(stderr, status);
/* 關閉輸入FITS檔案指標 */
fits_close_file(inputfptr, &status);
if (status) fits_report_error(stderr, status);
/* ------------------ 以上讀取完畢 ----------------------------*/
/* pix 內就是你的像元值啦! 看你要做什麼操作吧!*/
for(i=1;i<=npixels;i++)
{
/* 你的操作 */
}
/* 準備好輸出的檔名 */
sprintf(outputname,"output.fits");
/* 準備輸出,下面是建議的寫法,-32是float */
if(
check &&
!(fits_create_file(&outputfptr, outputname , &status )) &&
!(fits_create_img(outputfptr, -32, naxis, &(naxes[0]), &status))
)
{
/* 寫入 Header */
/*dateobs、timeobs,因為字串格式沒有修改,本來就OK,所以照樣寫入即可*/
fits_write_record(outputfptr, dateobs, &status);
fits_write_record(outputfptr, timeobs, &status);
fits_write_key(outputfptr, TFLOAT, "EXPTIME", &exptime, NULL, &status);
fits_write_key(outputfptr, TFLOAT, "CCDTEMP", &ccdtemp, NULL, &status);
fits_write_key(outputfptr, TSTRING,"EXAMPLE", "This is an example", NULL, &status);
/* 設定起始的像元的座標 */
firstpix[0]=1;firstpix[1]=1;
/* 寫入像元的數值 */
fits_write_pix(outputfptr, TFLOAT, firstpix, npixels, pix, &status);
if (status) fits_report_error(stderr, status);
}
/* 關閉輸出的FITS檔案指標 */
fits_close_file(outputfptr, &status);
if (status) fits_report_error(stderr, status);
/* 釋放動態配置的記憶體 */
free(pix);
return 0;
}