Выше я уже писал о работе с FITS-форматом в GNU Octave, однако, что это за формат такой и с чем его едят как работать с ним в своих приложениях на C не упомянул.
Краткое описание формата
Что такое FITS можно прочитать в
википедии, поэтому здесь сделаю лишь краткую выжимку. Итак, FITS (Flexible Image Transport System - гибкая система передачи изображений) - цифровой формат файлов используемый в науке для хранения, передачи и редактирования изображений и их метаданных. В основном этот формат используется в астрономии для хранения изображений. Помимо изображений он позволяет хранить табличные данные, а также различные дескрипторы (ключи). Причем в одном файле может быть несколько изображений, таблиц и ключей. Сами дескрипторы хранятся в «шапке» файла в ASCII-формате, что позволяет при помощи less/more/F3 в mc и т.п. получить информацию о содержимом файла, даже не имея средств для просмотра FITS.
Библиотеки и программное обеспечение для работы с FITS
Помимо специализированных
средств работы с FITS-файлами, многие существующие графические редакторы могут импортировать изображения из них (правда, теряя метаинформацию, а также ограничивая импорт одним изображением на файл, хотя их там может быть несколько).
Математические пакеты (Matlab, Octave и т.п.) имеют средства для чтения/записи FITS-файлов (но тоже эти средства зачастую ограниченны); естественно, для астрофизиков есть и
специальное ПО (например,
MIDAS,
Iraf и т.п.), позволяющее полноценно работать с разнообразным содержимым FITS-файлов.
Для работы в своих проектах можно выбрать одну из
множества библиотек, позволяющих работать с FITS-файлами (а можно и свой "велосипед" сделать). В языке C наиболее популярной (и полноценной) является библиотека
CFITSIO, вкратце о ней я и расскажу.
Краткое описание CFITSIO
На основе
примеров работы с cfitsio можно понять основные функции библиотеки. Для работы с файлами cfitsio представляет функции чтения и записи, позволяющие работать со сжатыми файлами, задавать маски файлов (а также - области, необходимые для считывания) в имени файла. Маской в имени файла можно также задать необходимые таблицы и/или ключи для импорта, а также выполнить элементарную правку ключей.
При работе с FITS-файлами следует помнить, что наибольшее количество одновременно открытых файлов равно 25 (оно определяется значением константы NIOBUF при компиляции библиотеки). Максимальное количество расширений (блоков с данными - изображениями, таблицами, ключами), с которым может работать библиотека, составляет 1000 для одного файла (определяется константой MAXHDU). Библиотека не работает с файлами, чей размер превышает 2.1ГБ. Также библиотека не работает с данными, имеющими разрядность выше 32 бит (в принципе, это понятно: современные АЦП в светоприемной аппаратуре обычно имеют разрядность в лучшем случае 16 бит).
Библиотека позволяет одновременно несколько раз открыть один и тот же файл (например, для параллельной работы с шапкой, таблицами и изображениями в файле). При работе с файлами, чья «шапка» содержит несколько разделов с ключами, cfitsio может работать лишь с одним из разделов, поэтому если одновременно нужно брать данные из нескольких, необходимо будет открыть файл нужное количество раз.
При чтении и записи изображений cfitsio автоматически преобразует значения по значениям стандартных ключей BSCALE и BZERO:
output value = (FITS value) * BSCALE + BZERO
FITS value = ((input value) - BZERO) / BSCALE
Работая с ключами, следует помнить, что стандарт FITS вносит ограничение на размеры ключа: весь ключ (имя, значение, комментарий) не может иметь длину больше 80 байт; имя ключа не может превышать 8 байт. Если необходимо вписать, например, более длинное имя ключа, или же имя ключа должно содержать непотребные нестандартные символы (пробелы, а также символы, не являющиеся латинскими буквами или символом подчеркивания), перед таким ключом ставится префикс HIERARCH. В этом случае максимальная длина ключа составляет 67 символов (однако, при этом некуда будет вписывать значение переменной и комментарий). CFITSIO прозрачно работает с ключами и читает/записывает даже такие нестандартные ключи. Если при записи ключа возникает какая-то ошибка (например, ключ+значение+комментарий не вписываются в 80 байт, cfitsio возвращает ошибку). Кстати, 80 байт - это ограничение на всю строку, а т.к. имя ключа обычно отделено от значения знаком равенства, окруженным пробелами, а комментарий - косой чертой, окруженной пробелами, полный ключ со значением и комментарием суммарно может занимать не более 74 байт. Т.к. 8 байт занимает сам ключ (кроме указанного выше исключения), на значение и комментарий остается 66 байт.
Использование CFITSIO в своих проектах
Не мудрствуя лукаво, опишу работу с библиотекой на примере своих «велосипедов»:
takepic - CLI-интерфейс для работы с камерой FLI Proline 9000, а также многострадальной
fitsview - смотрелки FITS-файлов с возможностями простейшего редактирования (все никак не допилю модуль обработки гартманнограмм, да и вообще что-то подзабыл я о нем).
В takepic я взял за основу пример из SDK разработчиков камеры, добавив в него нужный мне функционал. Для упрощения диагностирования ошибок при работе с библиотекой cfitsio используются обертки:
#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)
Если при выполнении функций cfitsio возвращает ошибку, генерируется сообщение об ошибке и внешняя функция, в которой произошла ошибка, завершается с возвращением -1. При записи ключа в случае ошибки просто генерируется сообщение об ошибке (т.к. это не так критично).
Сама функция записи FITS-файлов довольно проста: в качестве аргументов она принимает имя файла (filename), размеры полученного изображения (width, height) и указатель на массив с данными изображения (data). Так как изображение характеризуется двумя осями, необходимо создать массив из двух чисел, в который поместить ширину и высоту изображения (этот массив, naxes, мы передадим функции создания области с изображением в FITS-файле). Итак, предварительная подготовка:
long naxes[2] = {width, height};
fitsfile *fp;
TRYFITS(fits_create_file, &fp, filename);
TRYFITS(fits_create_img, fp, USHORT_IMG, 2, naxes);
Я создаю область изображения с типом unsigned short (т.к. для работы с камерой FLI его вполне хватает), однако, можно указать и другой тип изображения - cfitsio автоматически осуществит преобразование типов (главное - помнить, что преобразование из int в uint скорее всего, закончится ошибкой, если входная интенсивность будет превышать допустимый уровень в выходной).
Далее можно заполнить «шапку» файла необходимыми данными, например:
WRITEKEY(fp, TSTRING, "FILE", filename, "Input file original name");
WRITEKEY(fp, TSTRING, "DETECTOR", camera, "Detector model");
WRITEKEY(fp, TDOUBLE, "TEMPBODY", &t_ext, "Camera body temperature at exp. end (degr C)");
WRITEKEY(fp, TDOUBLE, "EXPTIME", &tmp, "actual exposition time (sec)");
WRITEKEY(fp, TSTRING, "OBJECT", objname, "Object name");
и т.д., и т.п. (помимо основных сведений я вписываю в «шапку» еще сведения о погодных условиях, координатах объекта и т.п.).
После заполнения «шапки» мы записываем изображение и закрываем файл: все готово!
TRYFITS(fits_write_img, fp, TUSHORT, 1, width * height, data);
TRYFITS(fits_close_file, fp);
В fitsview мне нужно было не только записывать, но и читать файлы. Я использовал все те же макросы-обертки, что и в takepic, но добавил еще одну:
#define FITSFUN(f, ...) \
do{ gboolean status = FALSE; \
int ret = f(__VA_ARGS__, &status); \
if(ret || status) \
fits_report_error(stderr, status); \
}while(0)
чтобы в некритических случаях не завершать работу вызывавшей функции.
Функция записи файла практически ничем не отличается от takepic, разве что ключи пишутся в формате «карты» - уже готовой строки (т.к. ключи имеют разные форматы и хранятся в GtkTree).
Чтение файла производится с параллельной обработкой ключей (т.к. некоторая информация из ключей используется в характеристиках изображения), кроме того, ключи сохраняются в GtkTree для возможности редактирования.
Итак, основные функции для чтения файла включают в себя открытие файла, получение количества «шапок» в файле, перемещение к первой, считывание и обработка ключей
fitsfile *fp; // gchar *filename, IMAGE *image - аргументы функции
int dtype, j, hdunum, keynum, morekeys;
char keyname[FLEN_KEYWORD], keyval[FLEN_VALUE];
char keycomment[FLEN_COMMENT], cdtype[32];
TRYFITS(fits_open_file, &fp, filename, READWRITE);
FITSFUN(fits_get_num_hdus, fp, &hdunum);
FITSFUN(fits_get_hdrpos ,fp, &keynum, &morekeys);
for (j = 1; j <= keynum; j++){
FITSFUN(fits_read_keyn, fp, j, keyname, keyval, keycomment);
dtype = guess_type(keyval, cdtype);
add_key(image, cdtype, keyname, keyval, keycomment, dtype);
}
Функция guess_type пытается определить тип данных: анализируется вид данных и последовательно вызываются функции преобразования строки в разные типы (double, long long). Если данные не подходят к типам double или long long, проверяется еще - не являются ли они комплексным числом. Если и комплексным не является, тип считается строковым.
Если файл содержит больше одной «шапки», считываются ключи и из других. При записи cfitsio автоматически разобьет ключи на нужное количество «шапок» (если ключей будет слишком много).
Далее мы получаем параметры изображения, считываем его (сразу преобразуя средствами cfitsio в тип float) и закрываем файл.
TRYFITS(fits_get_img_param, fp, 4, &dtype, &naxis, naxes);
TRYFITS(fits_read_img, fp, TFLOAT, 1, sz, &nullval, image->data, &stat);
TRYFITS(fits_close_file, fp);
Итак, работа с FITS-файлами при помощи cfitsio достаточно проста.