Add saving to FITS and XISF
This commit is contained in:
+147
@@ -492,3 +492,150 @@ bool readXISFHeader(const QString &path, ImageInfoData &info)
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConvertRunable::ConvertRunable(const QString &in, const QString &out) :
|
||||||
|
m_infile(in),
|
||||||
|
m_outfile(out)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void writeXISFImage(pcl::XISFWriter &writer, RawImage *rawimg)
|
||||||
|
{
|
||||||
|
const cv::Mat &cvmat = rawimg->mat();
|
||||||
|
T pclimg(rawimg->width(), rawimg->height(), cvmat.channels() == 1 ? pcl::ColorSpace::Gray : pcl::ColorSpace::RGB);
|
||||||
|
if(cvmat.channels() == 1)
|
||||||
|
{
|
||||||
|
memcpy(pclimg.PixelData(0), rawimg->data(), rawimg->size()*sizeof(typename T::sample));
|
||||||
|
}
|
||||||
|
if(cvmat.channels() == 3)
|
||||||
|
{
|
||||||
|
std::vector<cv::Mat> channels;
|
||||||
|
cv::split(cvmat, channels);
|
||||||
|
memcpy(pclimg.PixelData(0), channels[0].data, rawimg->size()*sizeof(typename T::sample));
|
||||||
|
memcpy(pclimg.PixelData(1), channels[1].data, rawimg->size()*sizeof(typename T::sample));
|
||||||
|
memcpy(pclimg.PixelData(2), channels[2].data, rawimg->size()*sizeof(typename T::sample));
|
||||||
|
}
|
||||||
|
writer.WriteImage(pclimg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeFITSImage(fitsfile *fw, RawImage *rawimage, ImageInfoData &imageinfo)
|
||||||
|
{
|
||||||
|
static QStringList skipKeys = {"SIMPLE", "BITPIX", "NAXIS", "NAXIS1", "NAXIS2", "NAXIS3", "BZERO", "BSCALE", "EXTEND"};
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
long firstpix[3] = {1,1,1};
|
||||||
|
|
||||||
|
int channels = rawimage->mat().channels();
|
||||||
|
int naxis = channels == 1 ? 2 : 3;
|
||||||
|
long naxes[3] = {(int)rawimage->width(), (int)rawimage->height(), rawimage->mat().channels()};
|
||||||
|
|
||||||
|
std::vector<cv::Mat> mat;
|
||||||
|
if(channels == 1)
|
||||||
|
mat.push_back(rawimage->mat());
|
||||||
|
else
|
||||||
|
cv::split(rawimage->mat(), mat);
|
||||||
|
|
||||||
|
switch(CV_MAT_DEPTH(rawimage->dataType()))
|
||||||
|
{
|
||||||
|
case CV_8U:
|
||||||
|
fits_create_img(fw, BYTE_IMG, naxis, naxes, &status);
|
||||||
|
for(int i=0; i<channels; i++)
|
||||||
|
{
|
||||||
|
firstpix[2] = i+1;
|
||||||
|
fits_write_pix(fw, TBYTE, firstpix, rawimage->size(), mat[i].data, &status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CV_16U:
|
||||||
|
fits_create_img(fw, USHORT_IMG, naxis, naxes, &status);
|
||||||
|
for(int i=0; i<channels; i++)
|
||||||
|
{
|
||||||
|
firstpix[2] = i+1;
|
||||||
|
fits_write_pix(fw, TUSHORT, firstpix, rawimage->size(), mat[i].data, &status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CV_32F:
|
||||||
|
fits_create_img(fw, FLOAT_IMG, naxis, naxes, &status);
|
||||||
|
for(int i=0; i<channels; i++)
|
||||||
|
{
|
||||||
|
firstpix[2] = i+1;
|
||||||
|
fits_write_pix(fw, TFLOAT, firstpix, rawimage->size(), mat[i].data, &status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for(const FITSRecord &record : imageinfo.fitsHeader)
|
||||||
|
{
|
||||||
|
if(skipKeys.contains(record.key))continue;
|
||||||
|
|
||||||
|
bool isdouble;
|
||||||
|
bool isint;
|
||||||
|
bool isbool = record.value.toString() == "T" || record.value.toString() == "F";
|
||||||
|
double vald = record.value.toDouble(&isdouble);
|
||||||
|
int valb = record.value.toString() == "T";
|
||||||
|
long long vall = record.value.toLongLong(&isint);
|
||||||
|
QByteArray str = record.value.toString().toLatin1();
|
||||||
|
if(isdouble)
|
||||||
|
fits_write_key(fw, TDOUBLE, record.key.data(), &vald, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
||||||
|
else if(isint)
|
||||||
|
fits_write_key(fw, TLONGLONG, record.key.data(), &vall, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
||||||
|
else if(isbool)
|
||||||
|
fits_write_key(fw, TLOGICAL, record.key.data(), &valb, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
||||||
|
else if(record.key == "COMMENT")
|
||||||
|
fits_write_comment(fw, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
||||||
|
else if(record.key == "HISTORY")
|
||||||
|
fits_write_history(fw, record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
||||||
|
else
|
||||||
|
fits_write_key(fw, TSTRING, record.key.data(), str.isEmpty() ? nullptr : str.data(), record.comment.isEmpty() ? nullptr : record.comment.data(), &status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertRunable::run()
|
||||||
|
{
|
||||||
|
ImageInfoData imageinfo;
|
||||||
|
RawImage *rawimage = nullptr;
|
||||||
|
if(m_infile.endsWith(".FITS", Qt::CaseInsensitive) || m_infile.endsWith(".FIT", Qt::CaseInsensitive))
|
||||||
|
loadFITS(m_infile, imageinfo, &rawimage);
|
||||||
|
if(m_infile.endsWith(".XISF", Qt::CaseInsensitive))
|
||||||
|
loadXISF(m_infile, imageinfo, &rawimage);
|
||||||
|
|
||||||
|
if(rawimage)
|
||||||
|
{
|
||||||
|
if(m_outfile.endsWith(".XISF", Qt::CaseInsensitive))
|
||||||
|
{
|
||||||
|
pcl::XISFOptions options;
|
||||||
|
pcl::FITSKeywordArray fitskeywords;
|
||||||
|
for(auto &record : imageinfo.fitsHeader)
|
||||||
|
{
|
||||||
|
pcl::FITSHeaderKeyword key(pcl::IsoString(record.key.data()), pcl::IsoString(record.value.toString().toLatin1().data()), pcl::IsoString(record.comment.data()));
|
||||||
|
fitskeywords.Append(key);
|
||||||
|
}
|
||||||
|
pcl::XISFWriter xisf;
|
||||||
|
xisf.Create(m_outfile.utf16(), 1);
|
||||||
|
xisf.WriteFITSKeywords(fitskeywords);
|
||||||
|
switch(CV_MAT_DEPTH(rawimage->dataType()))
|
||||||
|
{
|
||||||
|
case CV_8U:
|
||||||
|
writeXISFImage<pcl::UInt8Image>(xisf, rawimage);
|
||||||
|
break;
|
||||||
|
case CV_16U:
|
||||||
|
writeXISFImage<pcl::UInt16Image>(xisf, rawimage);
|
||||||
|
break;
|
||||||
|
case CV_32F:
|
||||||
|
writeXISFImage<pcl::Image>(xisf, rawimage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m_outfile.endsWith(".FITS", Qt::CaseInsensitive) || m_outfile.endsWith(".FIT", Qt::CaseInsensitive))
|
||||||
|
{
|
||||||
|
int status = 0;
|
||||||
|
fitsfile *fw;
|
||||||
|
if(QFileInfo(m_outfile).exists())QFile::remove(m_outfile);
|
||||||
|
fits_create_diskfile(&fw, m_outfile.toLocal8Bit().data(), &status);
|
||||||
|
writeFITSImage(fw, rawimage, imageinfo);
|
||||||
|
fits_close_file(fw, &status);
|
||||||
|
}
|
||||||
|
delete rawimage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,4 +21,13 @@ public:
|
|||||||
void run();
|
void run();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ConvertRunable : public QRunnable
|
||||||
|
{
|
||||||
|
QString m_infile;
|
||||||
|
QString m_outfile;
|
||||||
|
public:
|
||||||
|
ConvertRunable(const QString &in, const QString &out);
|
||||||
|
void run();
|
||||||
|
};
|
||||||
|
|
||||||
#endif // LOADRUNABLE_H
|
#endif // LOADRUNABLE_H
|
||||||
|
|||||||
+26
-1
@@ -14,6 +14,8 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
#include <QThreadPool>
|
||||||
|
#include "loadrunable.h"
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
@@ -298,13 +300,36 @@ void MainWindow::indexDir()
|
|||||||
|
|
||||||
void MainWindow::saveAs()
|
void MainWindow::saveAs()
|
||||||
{
|
{
|
||||||
QString file = QFileDialog::getSaveFileName(this, tr("Save as"), _lastDir, tr("Images (*.jpg *.png *.JPG *.PNG)"));
|
QString selectedFilter;
|
||||||
|
QString file = QFileDialog::getSaveFileName(this, tr("Save as"), _lastDir, tr("JPEG (*.jpg *.JPG);; PNG (*.png *.PNG);;FITS (*.fits *.FITS);;XISF (*.xisf *.XISF)"), &selectedFilter);
|
||||||
if(!file.isEmpty())
|
if(!file.isEmpty())
|
||||||
|
{
|
||||||
|
QFileInfo info(file);
|
||||||
|
if(info.suffix().isEmpty())
|
||||||
|
{
|
||||||
|
if(selectedFilter.contains("jpg"))file += ".jpg";
|
||||||
|
if(selectedFilter.contains("png"))file += ".png";
|
||||||
|
if(selectedFilter.contains("fits"))file += ".fits";
|
||||||
|
if(selectedFilter.contains("xisf"))file += ".xisf";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(file.endsWith(".fits") || file.endsWith(".xisf"))
|
||||||
|
{
|
||||||
|
convert(file);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
QImage img = m_imageGL->imageWidget()->renderToImage();
|
QImage img = m_imageGL->imageWidget()->renderToImage();
|
||||||
if(!img.isNull())
|
if(!img.isNull())
|
||||||
img.save(file);
|
img.save(file);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::convert(const QString &outfile)
|
||||||
|
{
|
||||||
|
QString file = m_ringList->currentImage()->name();
|
||||||
|
QThreadPool::globalInstance()->start(new ConvertRunable(file, outfile));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::markImage()
|
void MainWindow::markImage()
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ protected slots:
|
|||||||
void loadFile(int row);
|
void loadFile(int row);
|
||||||
void indexDir();
|
void indexDir();
|
||||||
void saveAs();
|
void saveAs();
|
||||||
|
void convert(const QString &outfile);
|
||||||
void markImage();
|
void markImage();
|
||||||
void unmarkImage();
|
void unmarkImage();
|
||||||
void markAndNext();
|
void markAndNext();
|
||||||
|
|||||||
@@ -233,6 +233,11 @@ RawImage::ImgType RawImage::type() const
|
|||||||
return CV2Type(m_img.type());
|
return CV2Type(m_img.type());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int RawImage::dataType() const
|
||||||
|
{
|
||||||
|
return m_img.type();
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t RawImage::norm() const
|
uint32_t RawImage::norm() const
|
||||||
{
|
{
|
||||||
switch(m_img.type())
|
switch(m_img.type())
|
||||||
@@ -288,3 +293,8 @@ float RawImage::thumbAspect() const
|
|||||||
{
|
{
|
||||||
return m_thumbAspect;
|
return m_thumbAspect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cv::Mat& RawImage::mat() const
|
||||||
|
{
|
||||||
|
return m_img;
|
||||||
|
}
|
||||||
|
|||||||
@@ -73,11 +73,13 @@ public:
|
|||||||
uint32_t height() const;
|
uint32_t height() const;
|
||||||
uint32_t size() const;
|
uint32_t size() const;
|
||||||
ImgType type() const;
|
ImgType type() const;
|
||||||
|
int dataType() const;
|
||||||
uint32_t norm() const;
|
uint32_t norm() const;
|
||||||
void* data();
|
void* data();
|
||||||
const void* data() const;
|
const void* data() const;
|
||||||
void convertToThumbnail();
|
void convertToThumbnail();
|
||||||
float thumbAspect() const;
|
float thumbAspect() const;
|
||||||
|
const cv::Mat& mat() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // RAWIMAGE_H
|
#endif // RAWIMAGE_H
|
||||||
|
|||||||
Reference in New Issue
Block a user