initial commit

This commit is contained in:
2020-01-21 23:36:07 +01:00
parent 32d76c4bc2
commit 7983d3a8b3
10 changed files with 2894 additions and 0 deletions
+1169
View File
File diff suppressed because it is too large Load Diff
+870
View File
@@ -0,0 +1,870 @@
/*!
\file cexif.h
*/
#ifndef CEXIF_H
#define CEXIF_H
#include <QString>
#include <QVariant>
#include <QDateTime>
#include <QImage>
#include <QMetaType>
#include <QList>
#include <QSqlDatabase>
/*!
\brief
\class cEXIFCompression cexif.h "cexif.h"
*/
class cEXIFCompression
{
public:
cEXIFCompression(const qint32& iID, const QString& szCompression);
qint32 m_iID; /*!< TODO: describe */
QString m_szCompression; /*!< TODO: describe */
};
Q_DECLARE_METATYPE(cEXIFCompression*)
/*!
\brief
\class cEXIFCompressionList cexif.h "cexif.h"
*/
class cEXIFCompressionList : public QList<cEXIFCompression*>
{
public:
cEXIFCompressionList();
/*!
\brief
\fn add
\param iID
\param szCompression
\return cEXIFCompression
*/
cEXIFCompression* add(const qint32& iID, const QString& szCompression);
/*!
\brief
\fn find
\param iID
\return cEXIFCompression
*/
cEXIFCompression* find(const qint32& iID);
/*!
\brief
\fn compression
\param iID
\return QString
*/
QString compression(const qint32& iID);
};
Q_DECLARE_METATYPE(cEXIFCompressionList)
/*!
\brief
\class cEXIFLightSource cexif.h "cexif.h"
*/
class cEXIFLightSource
{
public:
cEXIFLightSource(const qint32& iID, const QString& szLightSource);
qint32 m_iID; /*!< TODO: describe */
QString m_szLightSource; /*!< TODO: describe */
};
Q_DECLARE_METATYPE(cEXIFLightSource*)
/*!
\brief
\class cEXIFLightSourceList cexif.h "cexif.h"
*/
class cEXIFLightSourceList : public QList<cEXIFLightSource*>
{
public:
cEXIFLightSourceList();
/*!
\brief
\fn add
\param iID
\param szLightSource
\return cEXIFLightSource
*/
cEXIFLightSource* add(const qint32& iID, const QString& szLightSource);
/*!
\brief
\fn find
\param iID
\return cEXIFLightSource
*/
cEXIFLightSource* find(const qint32& iID);
/*!
\brief
\fn lightSource
\param iID
\return QString
*/
QString lightSource(const qint32& iID);
};
/*!
\brief
\class cEXIFFlash cexif.h "cexif.h"
*/
class cEXIFFlash
{
public:
cEXIFFlash(const qint32& iID, const QString& szFlash);
qint32 m_iID; /*!< TODO: describe */
QString m_szFlash; /*!< TODO: describe */
};
Q_DECLARE_METATYPE(cEXIFFlash*)
/*!
\brief
\class cEXIFFlashList cexif.h "cexif.h"
*/
class cEXIFFlashList : public QList<cEXIFFlash*>
{
public:
cEXIFFlashList();
/*!
\brief
\fn add
\param iID
\param szFlash
\return cEXIFFlash
*/
cEXIFFlash* add(const qint32& iID, const QString& szFlash);
/*!
\brief
\fn find
\param iID
\return cEXIFFlash
*/
cEXIFFlash* find(const qint32& iID);
/*!
\brief
\fn flash
\param iID
\return QString
*/
QString flash(const qint32& iID);
};
/**
* @brief
*
*/
class cEXIFTag
{
public:
cEXIFTag(const qint32& iTAGID, const QString& szTAGName, const QString& szIFD, const QString& szKey, const QString& szType, const QString& szDescription);
qint32 m_iTAGID; /*!< TODO: describe */
QString m_szTAGName; /*!< TODO: describe */
QString m_szIFD; /*!< TODO: describe */
QString m_szKey; /*!< TODO: describe */
QString m_szType; /*!< TODO: describe */
QString m_szDescription; /*!< TODO: describe */
};
Q_DECLARE_METATYPE(cEXIFTag*)
/*!
\brief
\class cEXIFTagList cexif.h "cexif.h"
*/
class cEXIFTagList : public QList<cEXIFTag*>
{
public:
cEXIFTagList();
/*!
\brief
\fn add
\param iTAGID
\param szTAGName
\param szIFD
\param szType
\param szDescription
\return cEXIFTag
*/
cEXIFTag* add(const qint32& iTAGID, const QString& szTAGName, const QString& szIFD, const QString& szKey, const QString& szType, const QString& szDescription);
/*!
\brief
\fn find
\param szKey
\return cEXIFTag
*/
cEXIFTag* find(const QString& szKey);
/*!
\brief
\fn find
\param iTAGID
\param szIFD
\return cEXIFTag
*/
cEXIFTag* find(const qint32& iTAGID, const QString& szIFD);
/*!
\brief
\fn find
\param szTAG
\param szIFD
\return cEXIFTag
*/
cEXIFTag* find(const QString& szTAG, const QString& szIFD);
};
/*!
\brief
*/
class cEXIFValue
{
public:
cEXIFValue(cEXIFTag* lpEXIFTag);
/*!
\brief
\fn exifTag
\return cEXIFTag
*/
cEXIFTag* exifTag();
/*!
\brief
\fn setValue
\param szValue
\param iTypeId
\param iCount
*/
void setValue(const QString& szValue, qint32 iTypeId, qint32 iCount = 1);
/*!
\brief
\fn value
\return QVariant
*/
QVariant value();
/*!
\brief
\fn valueList
\return QList<QVariant>
*/
QList<QVariant> valueList();
private:
cEXIFTag* m_lpEXIFTag; /*!< TODO: describe */
QList<QVariant> m_valueList; /*!< TODO: describe */
/*!
\brief
\fn convertValue
\param szValue
\param iTypeId
\return QVariant
*/
QVariant convertValue(const QString& szValue, qint32 iTypeId);
};
Q_DECLARE_METATYPE(cEXIFValue*)
/*!
\brief
\class cEXIFValueList cexif.h "cexif.h"
*/
class cEXIFValueList : public QList<cEXIFValue*>
{
public:
cEXIFValueList();
/*!
\brief
\fn add
\param lpEXIFTag
\return cEXIFValue
*/
cEXIFValue* add(cEXIFTag* lpEXIFTag);
/*!
\brief
\fn find
\param lpEXIFTag
\return cEXIFValue
*/
cEXIFValue* find(cEXIFTag* lpEXIFTag);
};
/**
* @brief
*
*/
class cIPTCTag
{
public:
cIPTCTag(const qint32& iTAGID, const QString& szTAGName, const qint32& iTypeID, const QString& szDescription);
qint32 m_iTAGID; /*!< TODO: describe */
QString m_szTAGName; /*!< TODO: describe */
qint32 m_iTypeID; /*!< TODO: describe */
QString m_szDescription; /*!< TODO: describe */
};
Q_DECLARE_METATYPE(cIPTCTag*)
/*!
\brief
\class cIPTCTagList cexif.h "cexif.h"
*/
class cIPTCTagList : public QList<cIPTCTag*>
{
public:
cIPTCTagList();
/*!
\brief
\fn add
\param iTAGID
\param szTAGName
\param iTypeID
\param szDescription
\return cIPTCTag
*/
cIPTCTag* add(const qint32& iTAGID, const QString& szTAGName, const qint32& iTypeID, const QString& szDescription);
/*!
\brief
\fn find
\param iTAGID
\return cIPTCTag
*/
cIPTCTag* find(const qint32& iTAGID);
};
/*!
\brief
*/
class cIPTCValue
{
public:
cIPTCValue(cIPTCTag* lpIPTCTag);
/*!
\brief
\fn iptcTag
\return cIPTCTag
*/
cIPTCTag* iptcTag();
/*!
\brief
\fn setValue
\param szValue
\param iTypeId
\param iCount
*/
void setValue(const QString& szValue, qint32 iTypeId, qint32 iCount = 1);
/*!
\brief
\fn value
\return QVariant
*/
QVariant value();
/*!
\brief
\fn valueList
\return QList<QVariant>
*/
QList<QVariant> valueList();
private:
cIPTCTag* m_lpIPTCTag; /*!< TODO: describe */
QList<QVariant> m_valueList; /*!< TODO: describe */
/*!
\brief
\fn convertValue
\param szValue
\param iTypeId
\return QVariant
*/
QVariant convertValue(const QString& szValue, qint32 iTypeId);
};
Q_DECLARE_METATYPE(cIPTCValue*)
/*!
\brief
\class cIPTCValueList cexif.h "cexif.h"
*/
class cIPTCValueList : public QList<cIPTCValue*>
{
public:
cIPTCValueList();
/*!
\brief
\fn add
\param lpIPTCTag
\return cIPTCValue
*/
cIPTCValue* add(cIPTCTag* lpIPTCTag);
/*!
\brief
\fn find
\param lpIPTCTag
\return cIPTCValue
*/
cIPTCValue* find(cIPTCTag* lpIPTCTag);
};
/**
* @brief
*
*/
class cXMPTag
{
public:
cXMPTag(const QString& szTAGName, const qint32& iTypeID, const QString& szDescription);
QString m_szTAGName; /*!< TODO: describe */
qint32 m_iTypeID; /*!< TODO: describe */
QString m_szDescription; /*!< TODO: describe */
};
Q_DECLARE_METATYPE(cXMPTag*)
/*!
\brief
\class cXMPTagList cexif.h "cexif.h"
*/
class cXMPTagList : public QList<cXMPTag*>
{
public:
cXMPTagList();
/*!
\brief
\fn add
\param szTAGName
\param iTypeID
\param szDescription
\return cXMPTag
*/
cXMPTag* add(const QString& szTAGName, const qint32& iTypeID, const QString& szDescription);
/*!
\brief
\fn find
\param szTAGName
\return cXMPTag
*/
cXMPTag* find(const QString& szTAGName);
};
/*!
\brief
*/
class cXMPValue
{
public:
cXMPValue(cXMPTag* lpXMPTag);
/*!
\brief
\fn exifTag
\return cXMPTag
*/
cXMPTag* xmpTag();
/*!
\brief
\fn setValue
\param szValue
\param iTypeId
\param iCount
*/
void setValue(const QString& szValue, qint32 iTypeId, qint32 iCount = 1);
/*!
\brief
\fn value
\return QVariant
*/
QVariant value();
/*!
\brief
\fn valueList
\return QList<QVariant>
*/
QList<QVariant> valueList();
private:
cXMPTag* m_lpXMPTag; /*!< TODO: describe */
QList<QVariant> m_valueList; /*!< TODO: describe */
/*!
\brief
\fn convertValue
\param szValue
\param iTypeId
\return QVariant
*/
QVariant convertValue(const QString& szValue, qint32 iTypeId);
};
Q_DECLARE_METATYPE(cXMPValue*)
/*!
\brief
\class cXMPValueList cexif.h "cexif.h"
*/
class cXMPValueList : public QList<cXMPValue*>
{
public:
cXMPValueList();
/*!
\brief
\fn add
\param lpXMPTag
\return cXMPValue
*/
cXMPValue* add(cXMPTag* lpXMPTag);
/*!
\brief
\fn find
\param lpXMPTag
\return cXMPValue
*/
cXMPValue* find(cXMPTag* lpXMPTag);
};
/*!
\brief
\class cEXIF cexif.h "cexif.h"
*/
class cEXIF
{
public:
cEXIF(cEXIFTagList* lpEXIFTagList, cEXIFCompressionList* lpEXIFCompressionList, cEXIFLightSourceList* lpEXIFLightSourceList, cEXIFFlashList* lpEXIFFlashList, cIPTCTagList* lpIPTCTagList, cXMPTagList* lpXMPTagList);
/*!
\brief
\fn fromFile
\param szFileName
\return bool
*/
bool fromFile(const QString& szFileName);
/*!
\brief
\fn mimeType
\return QString
*/
QString mimeType();
/*!
\brief
\fn imageWidth
\return qint32
*/
qint32 imageWidth();
/*!
\brief
\fn imageHeight
\return qint32
*/
qint32 imageHeight();
/*!
\brief
\fn imageOrientation
\return qint16
*/
qint16 imageOrientation();
/*!
\brief
\fn cameraMake
\return QString
*/
QString cameraMake();
/*!
\brief
\fn cameraModel
\return QString
*/
QString cameraModel();
/*!
\brief
\fn dateTime
\return QDateTime
*/
QDateTime dateTime();
/*!
\brief
\fn fNumber
\return QString
*/
QString fNumber();
/*!
\brief
\fn iso
\return qint32
*/
qint32 iso();
/*!
\brief
\fn flash
\return QString
*/
QString flash();
/*!
\brief
\fn flashID
\return qint32
*/
qint32 flashID();
/*!
\brief
\fn focalLength
\return qreal
*/
qreal focalLength();
/*!
\brief
\fn lensMake
\return QString
*/
QString lensMake();
/*!
\brief
\fn lensModel
\return QString
*/
QString lensModel();
/*!
\brief
\fn exposureTime
\return QString
*/
QString exposureTime();
/*!
\brief
\fn exposureBias
\return qint32
*/
qint32 exposureBias();
/*!
\brief
\fn exifVersion
\return QString
*/
QString exifVersion();
/*!
\brief
\fn dateTimeOriginal
\return QDateTime
*/
QDateTime dateTimeOriginal();
/*!
\brief
\fn dateTimeDigitized
\return QDateTime
*/
QDateTime dateTimeDigitized();
/*!
\brief
\fn whiteBalance
\return qint32
*/
qint32 whiteBalance();
/*!
\brief
\fn focalLength35
\return qreal
*/
qreal focalLength35();
/*!
\brief
\fn gps
\return QString
*/
QString gps();
/*!
\brief
\fn duration
\return QString
*/
QString duration();
/*!
\brief
\fn fileName
\return QString
*/
QString fileName();
/*!
\brief
\fn previewList
\return QList<QImage>
*/
QList<QImage> previewList();
/*!
\brief
\fn thumbnail
\return QImage
*/
QImage thumbnail();
/*!
\brief
\fn copyTo
\param fileName
*/
bool copyTo(const QString& fileName);
private:
cEXIFValueList m_exifValueList; /*!< TODO: describe */
cIPTCValueList m_iptcValueList; /*!< TODO: describe */
cXMPValueList m_xmpValueList; /*!< TODO: describe */
QString m_szMimeType; /*!< TODO: describe */
qint32 m_iWidth; /*!< TODO: describe */
qint32 m_iHeight; /*!< TODO: describe */
QString m_szFileName; /*!< TODO: describe */
QList<QImage> m_previewList; /*!< TODO: describe */
QImage m_thumbnail; /*!< TODO: describe */
cEXIFTagList* m_lpEXIFTagList; /*!< TODO: describe */
cEXIFCompressionList* m_lpEXIFCompressionList; /*!< TODO: describe */
cEXIFLightSourceList* m_lpEXIFLightSourceList; /*!< TODO: describe */
cEXIFFlashList* m_lpEXIFFlashList; /*!< TODO: describe */
cIPTCTagList* m_lpIPTCTagList; /*!< TODO: describe */
cXMPTagList* m_lpXMPTagList; /*!< TODO: describe */
/*!
\brief
\fn getEXIFTag
\param iTAGID
\param szIFD
\return QVariant
*/
QVariant getEXIFTag(qint32 iTAGID, const QString& szIFD);
/*!
\brief
\fn getIPTCTag
\param iTAGID
\return QVariant
*/
QVariant getIPTCTag(qint32 iTAGID);
/*!
\brief
\fn getXMPTag
\param szTAGName
\return QVariant
*/
QVariant getXMPTag(const QString& szTAGName);
/*!
\brief
\fn getTagList
\param iTAGID
\param iIFDID
\return QList<QVariant>
*/
QList<QVariant> getTagList(qint32 iTAGID, const QString& szIFD);
};
Q_DECLARE_METATYPE(cEXIF*)
#endif // CEXIF_H
+14
View File
@@ -0,0 +1,14 @@
#include "cfilebrowser.h"
#include "ui_cfilebrowser.h"
cFileBrowser::cFileBrowser(QWidget *parent) :
QWidget(parent),
ui(new Ui::cFileBrowser)
{
ui->setupUi(this);
}
cFileBrowser::~cFileBrowser()
{
delete ui;
}
+22
View File
@@ -0,0 +1,22 @@
#ifndef CFILEBROWSER_H
#define CFILEBROWSER_H
#include <QWidget>
namespace Ui {
class cFileBrowser;
}
class cFileBrowser : public QWidget
{
Q_OBJECT
public:
explicit cFileBrowser(QWidget *parent = nullptr);
~cFileBrowser();
private:
Ui::cFileBrowser *ui;
};
#endif // CFILEBROWSER_H
+21
View File
@@ -0,0 +1,21 @@
<ui version="4.0">
<author/>
<comment/>
<exportmacro/>
<class>cFileBrowser</class>
<widget name="cFileBrowser" class="QWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
</widget>
<pixmapfunction/>
<connections/>
</ui>
+14
View File
@@ -0,0 +1,14 @@
#include "cfileviewer.h"
#include "ui_cfileviewer.h"
cFileViewer::cFileViewer(QWidget *parent) :
QWidget(parent),
ui(new Ui::cFileViewer)
{
ui->setupUi(this);
}
cFileViewer::~cFileViewer()
{
delete ui;
}
+22
View File
@@ -0,0 +1,22 @@
#ifndef CFILEVIEWER_H
#define CFILEVIEWER_H
#include <QWidget>
namespace Ui {
class cFileViewer;
}
class cFileViewer : public QWidget
{
Q_OBJECT
public:
explicit cFileViewer(QWidget *parent = nullptr);
~cFileViewer();
private:
Ui::cFileViewer *ui;
};
#endif // CFILEVIEWER_H
+21
View File
@@ -0,0 +1,21 @@
<ui version="4.0">
<author/>
<comment/>
<exportmacro/>
<class>cFileViewer</class>
<widget name="cFileViewer" class="QWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
</widget>
<pixmapfunction/>
<connections/>
</ui>
+428
View File
@@ -0,0 +1,428 @@
/*!
\file cimage.cpp
*/
#include "cimage.h"
#include "opencv2/imgproc/types_c.h"
#include "opencv2/imgproc.hpp"
#include <QTransform>
#include <QFileInfo>
#include <QDebug>
#include <QElapsedTimer>
cImage::cImage() :
QImage(),
m_isChromatic(true),
m_camType(camera_unknown),
m_raw(false)
{
}
cImage::cImage(const QSize &size, QImage::Format format) :
QImage(size, format),
m_isChromatic(true),
m_camType(camera_unknown),
m_raw(false)
{
}
cImage::cImage(int width, int height, QImage::Format format) :
QImage(width, height, format),
m_isChromatic(true),
m_camType(camera_unknown),
m_raw(false)
{
}
cImage::cImage(uchar *data, int width, int height, QImage::Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo) :
QImage(data, width, height, format, cleanupFunction, cleanupInfo),
m_isChromatic(true),
m_camType(camera_unknown),
m_raw(false)
{
}
cImage::cImage(const uchar *data, int width, int height, QImage::Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo) :
QImage(data, width, height, format, cleanupFunction, cleanupInfo),
m_isChromatic(true),
m_camType(camera_unknown),
m_raw(false)
{
}
cImage::cImage(uchar *data, int width, int height, int bytesPerLine, QImage::Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo) :
QImage(data, width, height, bytesPerLine, format, cleanupFunction, cleanupInfo),
m_isChromatic(true),
m_camType(camera_unknown),
m_raw(false)
{
}
cImage::cImage(const uchar *data, int width, int height, int bytesPerLine, QImage::Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo) :
QImage(data, width, height, bytesPerLine, format, cleanupFunction, cleanupInfo),
m_isChromatic(true),
m_camType(camera_unknown),
m_raw(false)
{
}
cImage::cImage(const QString &fileName, const char *format) :
QImage(),
m_isChromatic(true),
m_camType(camera_unknown),
m_raw(false)
{
load(fileName, format);
}
cImage::cImage(const QImage &image) :
QImage(image),
m_isChromatic(true),
m_camType(camera_unknown),
m_raw(false)
{
}
cImage::cImage(QImage &&other) :
QImage(other),
m_isChromatic(true),
m_camType(camera_unknown),
m_raw(false)
{
}
bool cImage::load(const QString &fileName, const char *format)
{
QFileInfo fileInfo(fileName);
if(!fileInfo.suffix().compare("NEF", Qt::CaseInsensitive))
return(loadRAW(fileName));
if(QImage::load(fileName, format))
return(true);
return(loadRAW(fileName));
}
bool cImage::openBuffer(const QString &fileName, const QSharedPointer<QByteArray>& ba, LibRaw& iProcessor)
{
int error = LIBRAW_DATA_ERROR;
QFileInfo fi(fileName);
if(fi.suffix().contains("iiq", Qt::CaseInsensitive) || !ba || ba->isEmpty())
error = iProcessor.open_file(fileName.toStdString().c_str());
else
{
if(ba->isEmpty() || ba->size() < 100)
return(false);
error = iProcessor.open_buffer((void*)ba->constData(), static_cast<size_t>(ba->size()));
}
return (error == LIBRAW_SUCCESS);
}
void cImage::detectSpecialCamera(const LibRaw & iProcessor)
{
if(QString(iProcessor.imgdata.idata.model) == "IQ260 Achromatic")
m_isChromatic = false;
if (QString(iProcessor.imgdata.idata.model).contains("IQ260"))
m_camType = camera_iiq;
else if (!QString(iProcessor.imgdata.idata.make).compare("Canon", Qt::CaseInsensitive))
m_camType = camera_canon;
}
cv::Mat cImage::demosaic(LibRaw & iProcessor)
{
cv::Mat rawMat = cv::Mat(iProcessor.imgdata.sizes.height, iProcessor.imgdata.sizes.width, CV_16UC1);
double dynamicRange = static_cast<double>(iProcessor.imgdata.color.maximum - iProcessor.imgdata.color.black);
// normalize all image values
for(int rIdx = 0; rIdx < rawMat.rows; rIdx++)
{
unsigned short *ptrRaw = rawMat.ptr<unsigned short>(rIdx);
for (int cIdx = 0; cIdx < rawMat.cols; cIdx++)
{
int colIdx = iProcessor.COLOR(rIdx, cIdx);
double val = static_cast<double>(iProcessor.imgdata.image[(rawMat.cols*rIdx) + cIdx][colIdx]);
// normalize the value w.r.t the black point defined
val = (val - iProcessor.imgdata.color.black) / dynamicRange;
ptrRaw[cIdx] = clip<unsigned short>(val * USHRT_MAX); // for conversion to 16U
}
}
// no demosaicing
if(m_isChromatic)
{
unsigned long type = static_cast<unsigned long>(iProcessor.imgdata.idata.filters);
type = type & 255;
cv::Mat rgbImg;
//define bayer pattern
if(type == 180)
cvtColor(rawMat, rgbImg, CV_BayerBG2RGB); //bitmask 10 11 01 00 -> 3(G) 2(B) 1(G) 0(R) -> RG RG RG
else if(type == 30)
cvtColor(rawMat, rgbImg, CV_BayerRG2RGB); //bitmask 00 01 11 10 -> 0 1 3 2
else if(type == 225)
cvtColor(rawMat, rgbImg, CV_BayerGB2RGB); //bitmask 11 10 00 01
else if(type == 75)
cvtColor(rawMat, rgbImg, CV_BayerGR2RGB); //bitmask 01 00 10 11
else
{
qWarning() << "Wrong Bayer Pattern (not BG, RG, GB, GR)\n";
return cv::Mat();
}
rawMat = rgbImg;
}
// 16U (1 or 3 channeled) Mat
return(rawMat);
}
cv::Mat cImage::prepareImg(const LibRaw & iProcessor)
{
cv::Mat rawMat = cv::Mat(iProcessor.imgdata.sizes.height, iProcessor.imgdata.sizes.width, CV_16UC3, cv::Scalar(0));
double dynamicRange = static_cast<double>(iProcessor.imgdata.color.maximum - iProcessor.imgdata.color.black);
// normalization function
auto normalize = [&](double val)
{
val = (val - iProcessor.imgdata.color.black) / dynamicRange;
return clip<unsigned short>(val * USHRT_MAX);
};
for (int rIdx = 0; rIdx < rawMat.rows; rIdx++)
{
unsigned short *ptrI = rawMat.ptr<unsigned short>(rIdx);
for (int cIdx = 0; cIdx < rawMat.cols; cIdx++)
{
*ptrI = normalize(iProcessor.imgdata.image[rawMat.cols*rIdx + cIdx][0]);
ptrI++;
*ptrI = normalize(iProcessor.imgdata.image[rawMat.cols*rIdx + cIdx][1]);
ptrI++;
*ptrI = normalize(iProcessor.imgdata.image[rawMat.cols*rIdx + cIdx][2]);
ptrI++;
}
}
return rawMat;
}
void cImage::whiteBalance(const LibRaw & iProcessor, cv::Mat & img)
{
// white balance must not be empty at this point
cv::Mat wb = whiteMultipliers(iProcessor);
const float* wbp = wb.ptr<float>();
assert(wb.cols == 4);
for(int rIdx = 0; rIdx < img.rows; rIdx++)
{
unsigned short *ptr = img.ptr<unsigned short>(rIdx);
for (int cIdx = 0; cIdx < img.cols; cIdx++)
{
//apply white balance correction
unsigned short r = clip<unsigned short>(*ptr * wbp[0]);
unsigned short g = clip<unsigned short>(*(ptr+1) * wbp[1]);
unsigned short b = clip<unsigned short>(*(ptr+2) * wbp[2]);
//apply color correction
int cr = qRound(iProcessor.imgdata.color.rgb_cam[0][0] * r +
iProcessor.imgdata.color.rgb_cam[0][1] * g +
iProcessor.imgdata.color.rgb_cam[0][2] * b);
int cg = qRound(iProcessor.imgdata.color.rgb_cam[1][0] * r +
iProcessor.imgdata.color.rgb_cam[1][1] * g +
iProcessor.imgdata.color.rgb_cam[1][2] * b);
int cb = qRound(iProcessor.imgdata.color.rgb_cam[2][0] * r +
iProcessor.imgdata.color.rgb_cam[2][1] * g +
iProcessor.imgdata.color.rgb_cam[2][2] * b);
// clip & save color corrected values
*ptr = clip<unsigned short>(cr);
ptr++;
*ptr = clip<unsigned short>(cg);
ptr++;
*ptr = clip<unsigned short>(cb);
ptr++;
}
}
}
cv::Mat cImage::whiteMultipliers(const LibRaw & iProcessor)
{
// get camera white balance multipliers
cv::Mat wm(1, 4, CV_32FC1);
float* wmp = wm.ptr<float>();
for(int idx = 0; idx < wm.cols; idx++)
wmp[idx] = iProcessor.imgdata.color.cam_mul[idx];
if(wmp[3] == 0.0f)
wmp[3] = wmp[1]; // take green (usually its RGBG)
// normalize white balance multipliers
float w = static_cast<float>(cv::sum(wm)[0] / 4.0f);
float maxW = 1.0f;
//clipping according the camera model
//if w > 2.0 maxW is 256, otherwise 512
//tested empirically
//check if it can be defined by some metadata settings?
if(w > 2.0f)
maxW = 255.0f;
if(w > 2.0f && m_camType == camera_canon)
maxW = 511.0f; // some cameras would even need ~800 - why?
//normalize white point
wm /= maxW;
// 1 x 4 32FC1 white balance vector
return wm;
}
void cImage::gammaCorrection(const LibRaw & iProcessor, cv::Mat& img)
{
// white balance must not be empty at this point
cv::Mat gt = gammaTable(iProcessor);
const unsigned short* gammaLookup = gt.ptr<unsigned short>();
assert(gt.cols == USHRT_MAX);
for(int rIdx = 0; rIdx < img.rows; rIdx++)
{
unsigned short *ptr = img.ptr<unsigned short>(rIdx);
for(int cIdx = 0; cIdx < img.cols * img.channels(); cIdx++)
{
// values close to 0 are treated linear
if (ptr[cIdx] <= 5) // 0.018 * 255
ptr[cIdx] = static_cast<unsigned short>(qRound(ptr[cIdx] * static_cast<double>(iProcessor.imgdata.params.gamm[1]) / 255.0));
else
ptr[cIdx] = gammaLookup[ptr[cIdx]];
}
}
}
cv::Mat cImage::gammaTable(const LibRaw & iProcessor)
{
// OK this is an instance of reverse engineering:
// we found out that the values of (at least) the PhaseOne's achromatic back have to be doubled
// our images are no close to what their software (Capture One does) - only the gamma correction
// seems to be slightly different... -> now we can load compressed IIQs that are not supported by PS : )
double cameraHackMlp = (QString(iProcessor.imgdata.idata.model) == "IQ260 Achromatic") ? 2.0 : 1.0;
//read gamma value and create gamma table
double gamma = static_cast<double>(iProcessor.imgdata.params.gamm[0]);
cv::Mat gmt(1, USHRT_MAX, CV_16UC1);
unsigned short* gmtp = gmt.ptr<unsigned short>();
for (int idx = 0; idx < gmt.cols; idx++) {
gmtp[idx] = clip<unsigned short>(qRound((1.099*std::pow(static_cast<double>(idx) / USHRT_MAX, gamma) - 0.099) * 255 * cameraHackMlp));
}
// a 1 x 65535 U16 gamma table
return gmt;
}
QImage cImage::raw2Img(const LibRaw & iProcessor, cv::Mat & img)
{
//check the pixel aspect ratio of the raw image
if(iProcessor.imgdata.sizes.pixel_aspect != 1.0f)
cv::resize(img, img, cv::Size(), static_cast<double>(iProcessor.imgdata.sizes.pixel_aspect), 1.0f);
// revert back to 8-bit image
img.convertTo(img, CV_8U);
// TODO: for now - fix this!
if(img.channels() == 1)
cv::cvtColor(img, img, CV_GRAY2RGB);
return(mat2QImage(img));
}
QImage cImage::mat2QImage(cv::Mat img)
{
QImage qImg;
// since Mat header is copied, a new buffer should be allocated (check this!)
if(img.depth() == CV_32F)
img.convertTo(img, CV_8U, 255);
if(img.type() == CV_8UC1)
qImg = QImage(img.data, static_cast<int>(img.cols), static_cast<int>(img.rows), static_cast<int>(img.step), QImage::Format_Indexed8); // opencv uses size_t for scaling in x64 applications
if(img.type() == CV_8UC3)
qImg = QImage(img.data, static_cast<int>(img.cols), static_cast<int>(img.rows), static_cast<int>(img.step), QImage::Format_RGB888);
if(img.type() == CV_8UC4)
qImg = QImage(img.data, static_cast<int>(img.cols), static_cast<int>(img.rows), static_cast<int>(img.step), QImage::Format_ARGB32);
qImg = qImg.copy();
return qImg;
}
bool cImage::loadRAW(const QString &fileName)
{
QSharedPointer<QByteArray> ba;
LibRaw iProcessor;
if(!openBuffer(fileName, ba, iProcessor))
{
qDebug() << "could not open buffer";
return(false);
}
detectSpecialCamera(iProcessor);
int error = iProcessor.unpack();
if(strcmp(iProcessor.version(), "0.13.5") != 0) // fixes a bug specific to libraw 13 - version call is UNTESTED
iProcessor.raw2image();
if(error != LIBRAW_SUCCESS)
return(false);
cv::Mat rawMat;
if(iProcessor.imgdata.idata.filters)
rawMat = demosaic(iProcessor);
else
rawMat = prepareImg(iProcessor);
// color correction + white balance
if(m_isChromatic)
whiteBalance(iProcessor, rawMat);
gammaCorrection(iProcessor, rawMat);
// reduce color noise
// if (DkSettingsManager::param().resources().filterRawImages && mIsChromatic)
// reduceColorNoise(iProcessor, rawMat);
*this = raw2Img(iProcessor, rawMat);
iProcessor.recycle();
rawMat.release();
m_raw = true;
return(true);
}
bool cImage::isRaw()
{
return(m_raw);
}
+313
View File
@@ -0,0 +1,313 @@
/*!
\file cimage.h
*/
#ifndef CIMAGE_H
#define CIMAGE_H
#include "libraw/libraw.h"
#include <opencv2/opencv.hpp>
#include <QImage>
/*!
\brief
\class cImage cimage.h "cimage.h"
*/
class cImage : public QImage
{
public:
cImage();
/*!
\brief
\fn cImage
\param size
\param format
*/
cImage(const QSize &size, QImage::Format format);
/*!
\brief
\fn cImage
\param width
\param height
\param format
*/
cImage(int width, int height, QImage::Format format);
/*!
\brief
\fn cImage
\param data
\param width
\param height
\param format
\param cleanupFunction
\param cleanupInfo
*/
cImage(uchar *data, int width, int height, QImage::Format format, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr);
/*!
\brief
\fn cImage
\param data
\param width
\param height
\param format
\param cleanupFunction
\param cleanupInfo
*/
cImage(const uchar *data, int width, int height, QImage::Format format, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr);
/*!
\brief
\fn cImage
\param data
\param width
\param height
\param bytesPerLine
\param format
\param cleanupFunction
\param cleanupInfo
*/
cImage(uchar *data, int width, int height, int bytesPerLine, QImage::Format format, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr);
/*!
\brief
\fn cImage
\param data
\param width
\param height
\param bytesPerLine
\param format
\param cleanupFunction
\param cleanupInfo
*/
cImage(const uchar *data, int width, int height, int bytesPerLine, QImage::Format format, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr);
/*!
\brief
\fn cImage
\param fileName
\param format
*/
cImage(const QString &fileName, const char *format = nullptr);
/*!
\brief
\fn cImage
\param image
*/
cImage(const QImage &image);
/*!
\brief
\fn cImage
\param other
*/
cImage(QImage &&other);
/*!
\brief
\fn load
\param fileName
\param format
\return bool
*/
bool load(const QString &fileName, const char *format = nullptr);
/*!
\brief
\fn isRaw
\return bool
*/
bool isRaw();
protected:
/*!
\brief
\enum Cam
*/
enum Cam
{
camera_unknown = 0,
camera_iiq,
camera_canon,
camera_end
};
template <typename num>
/*!
\brief
\fn clip
\param val
\return num
*/
num clip(float val) const
{
int vr = qRound(val);
// trust me I'm an engineer @ -2
// with -2 we do not get pink in oversaturated areas
if (vr > std::numeric_limits<num>::max())
vr = std::numeric_limits<num>::max()-2;
if (vr < 0)
vr = 0;
return static_cast<num>(vr);
}
template <typename num>
/*!
\brief
\fn clip
\param val
\return num
*/
num clip(double val) const
{
int vr = qRound(val);
// trust me I'm an engineer @ -2
// with -2 we do not get pink in oversaturated areas
if (vr > std::numeric_limits<num>::max())
vr = std::numeric_limits<num>::max()-2;
if (vr < 0)
vr = 0;
return static_cast<num>(vr);
}
template <typename num>
/*!
\brief
\fn clip
\param val
\return num
*/
num clip(int val) const
{
int vr = qRound(static_cast<double>(val));
// trust me I'm an engineer @ -2
// with -2 we do not get pink in oversaturated areas
if (vr > std::numeric_limits<num>::max())
vr = std::numeric_limits<num>::max()-2;
if (vr < 0)
vr = 0;
return static_cast<num>(vr);
}
private:
bool m_isChromatic; /*!< TODO: describe */
Cam m_camType; /*!< TODO: describe */
bool m_raw; /*!< TODO: describe */
/*!
\brief
\fn loadRAW
\param fileName
\return bool
*/
bool loadRAW(const QString &fileName);
/*!
\brief
\fn openBuffer
\param fileName
\param ba
\param iProcessor
\return bool
*/
bool openBuffer(const QString &fileName, const QSharedPointer<QByteArray>& ba, LibRaw& iProcessor);
/*!
\brief
\fn detectSpecialCamera
\param iProcessor
*/
void detectSpecialCamera(const LibRaw & iProcessor);
/*!
\brief
\fn demosaic
\param iProcessor
\return cv::Mat
*/
cv::Mat demosaic(LibRaw & iProcessor);
/*!
\brief
\fn prepareImg
\param iProcessor
\return cv::Mat
*/
cv::Mat prepareImg(const LibRaw & iProcessor);
/*!
\brief
\fn whiteBalance
\param iProcessor
\param img
*/
void whiteBalance(const LibRaw & iProcessor, cv::Mat & img);
/*!
\brief
\fn whiteMultipliers
\param iProcessor
\return cv::Mat
*/
cv::Mat whiteMultipliers(const LibRaw & iProcessor);
/*!
\brief
\fn gammaCorrection
\param iProcessor
\param img
*/
void gammaCorrection(const LibRaw & iProcessor, cv::Mat& img);
/*!
\brief
\fn gammaTable
\param iProcessor
\return cv::Mat
*/
cv::Mat gammaTable(const LibRaw & iProcessor);
/*!
\brief
\fn raw2Img
\param iProcessor
\param img
\return QImage
*/
QImage raw2Img(const LibRaw & iProcessor, cv::Mat & img);
/*!
\brief
\fn mat2QImage
\param img
\return QImage
*/
QImage mat2QImage(cv::Mat img);
};
#endif // CIMAGE_H