il_fits.c
Upload User: wmy0603
Upload Date: 2022-05-02
Package Size: 1808k
Code Size: 11k
Development Platform:

Visual C++

  1. //-----------------------------------------------------------------------------
  2. //
  3. // ImageLib Sources
  4. // Copyright (C) 2000-2009 by Denton Woods
  5. // Last modified: 02/14/2009
  6. //
  7. // Filename: src-IL/src/il_fits.c
  8. //
  9. // Description: Reads from a Flexible Image Transport System (.fits) file.
  10. //                Specifications were found at 
  11. //                http://www.fileformat.info/format/fits.
  12. //
  13. //-----------------------------------------------------------------------------
  14. #include "il_internal.h"
  15. #ifndef IL_NO_FITS
  16. typedef struct FITSHEAD
  17. {
  18. ILboolean IsSimple;
  19. ILint BitsPixel;
  20. ILint NumAxes;  // Number of dimensions / axes
  21. ILint Width;
  22. ILint Height;
  23. ILint Depth;
  24. ILint NumChans;
  25. // Not in the header, but it keeps everything together.
  26. ILenum Type;
  27. ILenum Format;
  28. } FITSHEAD;
  29. enum {
  30. CARD_READ_FAIL = -1,
  31. CARD_END = 1,
  32. CARD_SIMPLE,
  33. CARD_NOT_SIMPLE,
  34. CARD_BITPIX,
  35. CARD_NUMAXES,
  36. CARD_AXIS,
  37. CARD_SKIP
  38. };
  39. ILboolean iIsValidFits(void);
  40. ILboolean iCheckFits(FITSHEAD *Header);
  41. ILboolean iLoadFitsInternal(void);
  42. ILenum GetCardImage(FITSHEAD *Header);
  43. ILboolean GetCardInt(char *Buffer, ILint *Val);
  44. //! Checks if the file specified in FileName is a valid FITS file.
  45. ILboolean ilIsValid_FITS(ILconst_string FileName)
  46. {
  47. ILHANDLE FitsFile;
  48. ILboolean bFits = IL_FALSE;
  49. if (!iCheckExtension(FileName, IL_TEXT("fits")) && !iCheckExtension(FileName, IL_TEXT("fit"))) {
  50. ilSetError(IL_INVALID_EXTENSION);
  51. return bFits;
  52. }
  53. FitsFile = iopenr(FileName);
  54. if (FitsFile == NULL) {
  55. ilSetError(IL_COULD_NOT_OPEN_FILE);
  56. return bFits;
  57. }
  58. bFits = ilIsValidF_FITS(FitsFile);
  59. icloser(FitsFile);
  60. return bFits;
  61. }
  62. //! Checks if the ILHANDLE contains a valid FITS file at the current position.
  63. ILboolean ilIsValidF_FITS(ILHANDLE File)
  64. {
  65. ILuint FirstPos;
  66. ILboolean bRet;
  67. iSetInputFile(File);
  68. FirstPos = itell();
  69. bRet = iIsValidFits();
  70. iseek(FirstPos, IL_SEEK_SET);
  71. return bRet;
  72. }
  73. //! Checks if Lump is a valid FITS lump.
  74. ILboolean ilIsValidL_FITS(const void *Lump, ILuint Size)
  75. {
  76. iSetInputLump(Lump, Size);
  77. return iIsValidFits();
  78. }
  79. // Internal function used to get the FITS header from the current file.
  80. ILboolean iGetFitsHead(FITSHEAD *Header)
  81. {
  82. ILenum CardKey;
  83. //@TODO: Use something other than memset?
  84. memset(Header, 0, sizeof(Header));  // Clear the header to all 0s first.
  85. do {
  86. CardKey = GetCardImage(Header);
  87. if (CardKey == CARD_END)  // End of the header
  88. break;
  89. if (CardKey == CARD_READ_FAIL)
  90. return IL_FALSE;
  91. if (CardKey == CARD_NOT_SIMPLE)
  92. return IL_FALSE;
  93. } while (!ieof());
  94. // The header should never reach the end of the file.
  95. if (ieof())
  96. return IL_FALSE;  // Error needed?
  97. // The header must always be a multiple of 2880, so we skip the padding bytes (spaces).
  98. iseek((2880 - (itell() % 2880)) % 2880, IL_SEEK_CUR);
  99. switch (Header->BitsPixel)
  100. {
  101. case 8:
  102. Header->Type = IL_UNSIGNED_BYTE;
  103. break;
  104. case 16:
  105. Header->Type = IL_SHORT;
  106. break;
  107. case 32:
  108. Header->Type = IL_INT;
  109. break;
  110. case -32:
  111. Header->Type = IL_FLOAT;
  112. break;
  113. case -64:
  114. Header->Type = IL_DOUBLE;
  115. break;
  116. default:
  117. ilSetError(IL_INVALID_FILE_HEADER);
  118. return IL_FALSE;
  119. }
  120. switch (Header->NumAxes)
  121. {
  122. case 1:  // Just a 1D image
  123. Header->Format = IL_LUMINANCE;
  124. Header->Height = 1;
  125. Header->Depth = 1;
  126. Header->NumChans = 1;
  127. break;
  128. case 2:  // Assuming it is a 2D image (width+height)
  129. Header->Format = IL_LUMINANCE;
  130. Header->Depth = 1;
  131. Header->NumChans = 1;
  132. break;
  133. case 3:
  134. // We cannot deal with more than 3 channels in an image.
  135. Header->Format = IL_LUMINANCE;
  136. Header->NumChans = 1;
  137. break;
  138. default:
  139. ilSetError(IL_INVALID_FILE_HEADER);
  140. return IL_FALSE;
  141. }
  142. return IL_TRUE;
  143. }
  144. // Internal function to get the header and check it.
  145. ILboolean iIsValidFits(void)
  146. {
  147. FITSHEAD Header;
  148. ILuint Pos = itell();
  149. if (!iGetFitsHead(&Header))
  150. return IL_FALSE;
  151. // The length of the header varies, so we just go back to the original position.
  152. iseek(Pos, IL_SEEK_CUR);
  153. return iCheckFits(&Header);
  154. }
  155. // Internal function used to check if the HEADER is a valid FITS header.
  156. ILboolean iCheckFits(FITSHEAD *Header)
  157. {
  158. switch (Header->BitsPixel)
  159. {
  160. case 8:  // These are the only values accepted.
  161. case 16:
  162. case 32:
  163. case -32:
  164. case -64:
  165. break;
  166. default:
  167. return IL_FALSE;
  168. }
  169. switch (Header->NumAxes)
  170. {
  171. case 1:  // Just a 1D image
  172. case 2:  // Assuming it is a 2D image (width+height)
  173. case 3:  // 3D image (with depth)
  174. break;
  175. default:
  176. return IL_FALSE;
  177. }
  178. // Possibility that one of these values is returned as <= 0 by atoi, which we cannot use.
  179. if (Header->Width <= 0 || Header->Height <= 0 || Header->Depth <= 0) {
  180. ilSetError(IL_INVALID_FILE_HEADER);
  181. return IL_FALSE;
  182. }
  183. return IL_TRUE;
  184. }
  185. //! Reads a FITS file
  186. ILboolean ilLoad_FITS(ILconst_string FileName)
  187. {
  188. ILHANDLE FitsFile;
  189. ILboolean bFits = IL_FALSE;
  190. FitsFile = iopenr(FileName);
  191. if (FitsFile == NULL) {
  192. ilSetError(IL_COULD_NOT_OPEN_FILE);
  193. return bFits;
  194. }
  195. bFits = ilLoadF_FITS(FitsFile);
  196. icloser(FitsFile);
  197. return bFits;
  198. }
  199. //! Reads an already-opened FITS file
  200. ILboolean ilLoadF_FITS(ILHANDLE File)
  201. {
  202. ILuint FirstPos;
  203. ILboolean bRet;
  204. iSetInputFile(File);
  205. FirstPos = itell();
  206. bRet = iLoadFitsInternal();
  207. iseek(FirstPos, IL_SEEK_SET);
  208. return bRet;
  209. }
  210. //! Reads from a memory "lump" that contains a FITS
  211. ILboolean ilLoadL_FITS(const void *Lump, ILuint Size)
  212. {
  213. iSetInputLump(Lump, Size);
  214. return iLoadFitsInternal();
  215. }
  216. // Internal function used to load the FITS.
  217. ILboolean iLoadFitsInternal(void)
  218. {
  219. FITSHEAD Header;
  220. ILuint i, NumPix;
  221. ILfloat MaxF = 0.0f;
  222. ILdouble MaxD = 0.0f;
  223. if (iCurImage == NULL) {
  224. ilSetError(IL_ILLEGAL_OPERATION);
  225. return IL_FALSE;
  226. }
  227. if (!iGetFitsHead(&Header))
  228. return IL_FALSE;
  229. if (!iCheckFits(&Header))
  230. return IL_FALSE;
  231. if (!ilTexImage(Header.Width, Header.Height, Header.Depth, Header.NumChans, Header.Format, Header.Type, NULL))
  232. return IL_FALSE;
  233. /*if (iread(iCurImage->Data, 1, iCurImage->SizeOfData) != iCurImage->SizeOfData)
  234. return IL_FALSE;*/
  235. NumPix = Header.Width * Header.Height * Header.Depth;
  236. //@TODO: Do some checks while reading to see if we have hit the end of the file.
  237. switch (Header.Type)
  238. {
  239. case IL_UNSIGNED_BYTE:
  240. if (iread(iCurImage->Data, 1, iCurImage->SizeOfData) != iCurImage->SizeOfData)
  241. return IL_FALSE;
  242. break;
  243. case IL_SHORT:
  244. for (i = 0; i < NumPix; i++) {
  245. ((ILshort*)iCurImage->Data)[i] = GetBigShort();
  246. }
  247. break;
  248. case IL_INT:
  249. for (i = 0; i < NumPix; i++) {
  250. ((ILint*)iCurImage->Data)[i] = GetBigInt();
  251. }
  252. break;
  253. case IL_FLOAT:
  254. for (i = 0; i < NumPix; i++) {
  255. ((ILfloat*)iCurImage->Data)[i] = GetBigFloat();
  256. if (((ILfloat*)iCurImage->Data)[i] > MaxF)
  257. MaxF = ((ILfloat*)iCurImage->Data)[i];
  258. }
  259. // Renormalize to [0..1].
  260. for (i = 0; i < NumPix; i++) {
  261. // Change all negative numbers to 0.
  262. if (((ILfloat*)iCurImage->Data)[i] < 0.0f)
  263. ((ILfloat*)iCurImage->Data)[i] = 0.0f;
  264. // Do the renormalization now, dividing by the maximum value.
  265. ((ILfloat*)iCurImage->Data)[i] = ((ILfloat*)iCurImage->Data)[i] / MaxF;
  266. }
  267. break;
  268. case IL_DOUBLE:
  269. for (i = 0; i < NumPix; i++) {
  270. ((ILdouble*)iCurImage->Data)[i] = GetBigDouble();
  271. if (((ILdouble*)iCurImage->Data)[i] > MaxD)
  272. MaxD = ((ILdouble*)iCurImage->Data)[i];
  273. }
  274. // Renormalize to [0..1].
  275. for (i = 0; i < NumPix; i++) {
  276. // Change all negative numbers to 0.
  277. if (((ILdouble*)iCurImage->Data)[i] < 0.0f)
  278. ((ILdouble*)iCurImage->Data)[i] = 0.0f;
  279. // Do the renormalization now, dividing by the maximum value.
  280. ((ILdouble*)iCurImage->Data)[i] = ((ILdouble*)iCurImage->Data)[i] / MaxD;
  281. } break;
  282. }
  283. return ilFixImage();
  284. }
  285. //@TODO: NAXISx have to come in order.  Check this!
  286. ILenum GetCardImage(FITSHEAD *Header)
  287. {
  288. char Buffer[80];
  289. if (iread(Buffer, 1, 80) != 80)  // Each card image is exactly 80 bytes long.
  290. return CARD_READ_FAIL;
  291. //@TODO: Use something other than !strncmp?
  292. if (!strncmp(Buffer, "END ", 4))
  293. return CARD_END;
  294. else if (!strncmp(Buffer, "SIMPLE ", 7)) {
  295. // The true value 'T' is always in the 30th position.
  296. if (Buffer[29] != 'T') {
  297. // We cannot support FITS files that do not correspond to the standard.
  298. Header->IsSimple = IL_FALSE;  //@TODO: Does this even need to be set?  Should exit loading anyway.
  299. ilSetError(IL_FORMAT_NOT_SUPPORTED);
  300. return CARD_NOT_SIMPLE;
  301. }
  302. Header->IsSimple = IL_TRUE;
  303. return CARD_SIMPLE;
  304. }
  305. else if (!strncmp(Buffer, "BITPIX ", 7)) {
  306. // The specs state that BITPIX has to come after SIMPLE.
  307. if (Header->IsSimple != IL_TRUE) {
  308. ilSetError(IL_INVALID_FILE_HEADER);
  309. return CARD_READ_FAIL;
  310. }
  311. if (GetCardInt(Buffer, &Header->BitsPixel) != IL_TRUE)
  312. return CARD_READ_FAIL;
  313. //@TODO: Should I do this check from the calling function?  Does it really matter?
  314. if (Header->BitsPixel == 0) {
  315. ilSetError(IL_FORMAT_NOT_SUPPORTED);
  316. return CARD_READ_FAIL;
  317. }
  318. return CARD_BITPIX;
  319. }
  320. // Needs the space after NAXIS so that it does not get this confused with NAXIS1, NAXIS2, etc.
  321. else if (!strncmp(Buffer, "NAXIS ", 6)) {
  322. if (GetCardInt(Buffer, &Header->NumAxes) != IL_TRUE)
  323. return CARD_READ_FAIL;
  324. //@TODO: Should I do this check from the calling function?  Does it really matter?
  325. if (Header->NumAxes < 1 || Header->NumAxes > 3) {
  326. ilSetError(IL_FORMAT_NOT_SUPPORTED);
  327. return CARD_READ_FAIL;
  328. }
  329. return CARD_NUMAXES;
  330. }
  331. else if (!strncmp(Buffer, "NAXIS1 ", 7)) {
  332. if (Header->NumAxes == 0) {  // Has not been initialized, and it has to come first.
  333. ilSetError(IL_INVALID_FILE_HEADER);
  334. return CARD_READ_FAIL;
  335. }
  336. // First one will always be the width.
  337. if (GetCardInt(Buffer, &Header->Width) != IL_TRUE)
  338. return CARD_READ_FAIL;
  339. return CARD_AXIS;
  340. }
  341. else if (!strncmp(Buffer, "NAXIS2 ", 7)) {
  342. if (Header->NumAxes == 0) {  // Has not been initialized, and it has to come first.
  343. ilSetError(IL_INVALID_FILE_HEADER);
  344. return CARD_READ_FAIL;
  345. }
  346. // Cannot have a 2nd axis for 0 or 1.
  347. if (Header->NumAxes == 0 || Header->NumAxes == 1) {
  348. ilSetError(IL_INVALID_FILE_HEADER);
  349. return CARD_READ_FAIL;
  350. }
  351. //@TODO: We are assuming that this is the height right now.  Could it just be a
  352. //  1D image with multiple bytes per pixel?
  353. if (GetCardInt(Buffer, &Header->Height) != IL_TRUE)
  354. return CARD_READ_FAIL;
  355. return CARD_AXIS;
  356. }
  357. else if (!strncmp(Buffer, "NAXIS3 ", 7)) {
  358. if (Header->NumAxes == 0) {  // Has not been initialized, and it has to come first.
  359. ilSetError(IL_INVALID_FILE_HEADER);
  360. return CARD_READ_FAIL;
  361. }
  362. // Cannot have a 3rd axis for 0, 1 and 2.
  363. if (Header->NumAxes < 3) {
  364. ilSetError(IL_INVALID_FILE_HEADER);
  365. return CARD_READ_FAIL;
  366. }
  367. if (GetCardInt(Buffer, &Header->Depth) != IL_TRUE)
  368. return CARD_READ_FAIL;
  369. return CARD_AXIS;
  370. }
  371. return CARD_SKIP;  // This is a card that we do not recognize, so skip it.
  372. }
  373. ILboolean GetCardInt(char *Buffer, ILint *Val)
  374. {
  375. ILuint i;
  376. char ValString[22];
  377. if (Buffer[7] != '=' && Buffer[8] != '=')
  378. return IL_FALSE;
  379. for (i = 9; i < 30; i++) {
  380. if (Buffer[i] != ' ' && Buffer[i] != 0)  // Right-aligned with ' ' or 0, so skip.
  381. break;
  382. }
  383. if (i == 30)  // Did not find the integer in positions 10 - 30.
  384. return IL_FALSE;
  385.   //@TODO: Safest way to do this?
  386. memcpy(ValString, &Buffer[i], 30-i);
  387. ValString[30-i] = 0;  // Terminate the string.
  388. //@TODO: Check the return somehow?
  389. *Val = atoi(ValString);
  390. return IL_TRUE;
  391. }
  392. #endif//IL_NO_FITS