Add image blob and bitpix enum
This commit is contained in:
parent
b56beba4ce
commit
366521853c
43
src/Bitpix.php
Normal file
43
src/Bitpix.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Dumbastro\FitsPhp;
|
||||
|
||||
/**
|
||||
* Valid BITPIX values
|
||||
*/
|
||||
enum Bitpix: int
|
||||
{
|
||||
case Uint8 = 8;
|
||||
case Uint16 = 16;
|
||||
case Uint32 = 32;
|
||||
case Uint64 = 64;
|
||||
case Float32 = -32;
|
||||
case Float64 = -64;
|
||||
|
||||
public function type(): string
|
||||
{
|
||||
return match($this) {
|
||||
Bitpix::Uint8 => 'int8',
|
||||
Bitpix::Uint16 => 'int16',
|
||||
Bitpix::Uint32 => 'int32',
|
||||
Bitpix::Uint64 => 'int64',
|
||||
Bitpix::Float32 => 'float32',
|
||||
Bitpix::Float64 => 'float64',
|
||||
};
|
||||
}
|
||||
|
||||
public function toString(): string
|
||||
{
|
||||
return match($this) {
|
||||
Bitpix::Uint8 => 'Character or unsigned binary integer',
|
||||
Bitpix::Uint16 => '16 bit two\'s complement binary integer',
|
||||
Bitpix::Uint32 => '32 bit two\'s complement binary integer',
|
||||
Bitpix::Uint64 => '64 bit two\'s complement binary integer',
|
||||
Bitpix::Float32 => 'IEEE single-precision floating point',
|
||||
Bitpix::Float64 => 'IEEE double-precision floating point',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
13
src/Exceptions/InvalidBitpixValue.php
Normal file
13
src/Exceptions/InvalidBitpixValue.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Dumbastro\FitsPhp\Exceptions;
|
||||
|
||||
class InvalidBitpixValue extends \Exception
|
||||
{
|
||||
public function __construct(int $bitpix)
|
||||
{
|
||||
$this->message = "The value $bitpix is not a valid BITPIX value";
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Dumbastro\FitsPhp\Exceptions;
|
||||
|
||||
final class InvalidFitsException extends \Exception implements \Throwable
|
||||
final class InvalidFits extends \Exception implements \Throwable
|
||||
{
|
||||
}
|
||||
|
@ -7,6 +7,6 @@ namespace Dumbastro\FitsPhp\Exceptions;
|
||||
/**
|
||||
* @todo Different exception if path is not writable?
|
||||
*/
|
||||
final class InvalidPathException extends \Exception
|
||||
final class InvalidPath extends \Exception
|
||||
{
|
||||
}
|
44
src/Fits.php
44
src/Fits.php
@ -5,38 +5,39 @@ declare(strict_types=1);
|
||||
namespace Dumbastro\FitsPhp;
|
||||
|
||||
use Dumbastro\FitsPhp\Exceptions\{
|
||||
InvalidFitsException,
|
||||
InvalidPathException,
|
||||
InvalidFits,
|
||||
InvalidPath,
|
||||
};
|
||||
|
||||
class Fits
|
||||
{
|
||||
/**
|
||||
* @var resource $byteStream
|
||||
*/
|
||||
private $byteStream;
|
||||
private string $contents;
|
||||
private string $path;
|
||||
private FitsHeader $fitsHeader;
|
||||
public readonly int $size;
|
||||
public readonly string $headerBlock;
|
||||
public readonly string $imageBlob;
|
||||
|
||||
/**
|
||||
* @throws InvalidFitsException, InvalidPathException
|
||||
* @throws InvalidFits, InvalidPath
|
||||
* @todo Check path for reading/writing errors
|
||||
*/
|
||||
public function __construct(string $path)
|
||||
{
|
||||
if (! is_readable($path) || ! is_file($path)) {
|
||||
throw new InvalidPathException("The path '$path' is not readable or is not a file.");
|
||||
throw new InvalidPath("The path '$path' is not readable or is not a file.");
|
||||
}
|
||||
$this->byteStream = fopen($path, 'rb');
|
||||
$this->contents = file_get_contents($path);
|
||||
$this->path = $path;
|
||||
$this->size = filesize($this->path);
|
||||
|
||||
if (! $this->validate()) {
|
||||
throw new InvalidFitsException('The opened file is not a valid FITS image (invalid block size)');
|
||||
throw new InvalidFits('The opened file is not a valid FITS image (invalid block size)');
|
||||
}
|
||||
|
||||
$this->headerBlock = $this->extractHeader();
|
||||
$this->fitsHeader = new FitsHeader($this->headerBlock);
|
||||
$this->imageBlob = $this->extractImageBlob();
|
||||
}
|
||||
/**
|
||||
* Validate the given FITS file based on block sizes
|
||||
@ -66,12 +67,29 @@ class Fits
|
||||
*/
|
||||
private function extractHeader(): string
|
||||
{
|
||||
$contents = fread($this->byteStream, $this->size);
|
||||
$end = strpos($contents, 'END');
|
||||
$end = strpos($this->contents, 'END');
|
||||
// Determine minimum integer number of blocks including 'END' position
|
||||
$headerEnd = (($end - ($end % 2880)) / 2880 + 1) * 2880;
|
||||
|
||||
return substr($contents, 0, $headerEnd);
|
||||
return substr($this->contents, 0, $headerEnd);
|
||||
}
|
||||
/**
|
||||
* Extract the FITS image blob as a string;
|
||||
* it uses the NAXIS1 and NAXIS2 keywords
|
||||
* to compute the length of the main data table
|
||||
*/
|
||||
private function extractImageBlob(): string
|
||||
{
|
||||
$naxis1 = (int)trim($this->fitsHeader->getKeywordValue('NAXIS1'));
|
||||
$naxis2 = (int)trim($this->fitsHeader->getKeywordValue('NAXIS2'));
|
||||
|
||||
$blobEnd = $naxis1 * $naxis2;
|
||||
|
||||
return substr(
|
||||
$this->contents,
|
||||
strlen($this->headerBlock) + 1,
|
||||
$blobEnd
|
||||
);
|
||||
}
|
||||
|
||||
public function writeTo(string $path): void
|
||||
|
@ -71,5 +71,36 @@ class FitsHeader
|
||||
|
||||
return $keywordsString . $blanks;
|
||||
}
|
||||
/**
|
||||
* Retrieve a Keyword object base on key name
|
||||
*/
|
||||
public function keyword(string $key): ?Keyword
|
||||
{
|
||||
$keyword = null;
|
||||
|
||||
foreach ($this->keywords as $k) {
|
||||
if (trim($k->name) === $key) {
|
||||
$keyword = $k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $keyword;
|
||||
}
|
||||
/**
|
||||
* Note: the keyword key string is case-sensitive
|
||||
*/
|
||||
public function getKeywordValue(string $key): string
|
||||
{
|
||||
$value = '';
|
||||
foreach ($this->keywords as $keyword) {
|
||||
if (trim($keyword->name) === $key) {
|
||||
$value = $keyword->value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
51
src/ImageBlob.php
Normal file
51
src/ImageBlob.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Dumbastro\FitsPhp;
|
||||
|
||||
use Dumbastro\FitsPhp\Exceptions\InvalidBitpixValue;
|
||||
use Dumbastro\FitsPhp\Bitpix;
|
||||
|
||||
class ImageBlob
|
||||
{
|
||||
private string $blob;
|
||||
private FitsHeader $header;
|
||||
public readonly Bitpix $bitpix;
|
||||
public readonly int $dataBits;
|
||||
|
||||
/**
|
||||
* @throws InvalidBitpixValue
|
||||
* @todo Don't assume that NAXIS = 2...
|
||||
*/
|
||||
public function __construct(FitsHeader $header, string $blob)
|
||||
{
|
||||
$this->header = $header;
|
||||
$this->blob = $blob;
|
||||
$bitpix = (int) $this->header->keyword('BITPIX')->value;
|
||||
$this->bitpix = Bitpix::tryFrom($bitpix) ?? throw new InvalidBitpixValue($bitpix);
|
||||
$naxis1 = (int) trim($this->header->keyword('NAXIS1')->value);
|
||||
$naxis2 = (int) trim($this->header->keyword('NAXIS2')->value);
|
||||
$this->dataBits = abs($this->bitpix->value) * $naxis1 * $naxis2;
|
||||
}
|
||||
/**
|
||||
* Returns a generator that yields image data
|
||||
* byte by byte
|
||||
*/
|
||||
public function dataBytes(): \Generator
|
||||
{
|
||||
for ($i = 0; $i < strlen($this->blob); $i++) {
|
||||
yield $this->blob[$i];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Convert to PNG
|
||||
* @todo Manipulate bits and add PNG header to blob??
|
||||
* Throw exception if gd fails?
|
||||
*/
|
||||
public function toPNG(int $quality = -1): bool
|
||||
{
|
||||
$gdImg = imagecreatefromstring($this->blob);
|
||||
return imagepng($gdImg, quality: $quality);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user