Update LibXISF, fixes in RawImage

This commit is contained in:
2023-08-28 20:37:11 +02:00
parent 9ffbdcee30
commit f1a2aae9b6
7 changed files with 336 additions and 99 deletions
+175 -66
View File
@@ -1,12 +1,16 @@
#include "rawimage.h"
#include <QDebug>
#include <cstring>
#include <QElapsedTimer>
int THUMB_SIZE = 128;
int THUMB_SIZE_BORDER = 138;
int THUMB_SIZE_BORDER_Y = 158;
double SATURATION = 0.95;
template<typename T, int ch>
void fromPlanarSSE(const void *in, void *out, size_t count);
size_t RawImage::typeSize(RawImage::DataType type)
{
switch(type)
@@ -48,7 +52,6 @@ RawImage::RawImage(const RawImage &d)
allocate(d.m_width, d.m_height, d.m_channels, d.m_type);
std::memcpy(m_pixels.get(), d.m_pixels.get(), m_width * m_height * m_ch * typeSize(m_type));
m_stats = d.m_stats;
m_saturated = d.m_saturated;
}
RawImage::RawImage(RawImage &&d)
@@ -63,7 +66,6 @@ RawImage::RawImage(RawImage &&d)
m_origType = d.m_origType;
m_stats = d.m_stats;
m_thumbAspect = d.m_thumbAspect;
m_saturated = d.m_saturated;
}
RawImage::RawImage(const QImage &img)
@@ -115,18 +117,86 @@ RawImage::RawImage(const QImage &img)
m_stats.m_stats = false;
}
bool RawImage::imageStats(double *mean, double *stdDev, double *median, double *min, double *max, double *mad, uint32_t *saturated)
const RawImage::Stats& RawImage::imageStats()
{
if(!m_stats.m_stats)calcStats();
if(mean)*mean = m_stats.m_mean[0];
if(stdDev)*stdDev = m_stats.m_stdDev[0];
if(median)*median = m_stats.m_median[0];
if(min)*min = m_stats.m_min[0];
if(max)*max = m_stats.m_max[0];
if(mad)*mad = m_stats.m_mad[0];
if(saturated)*saturated = m_saturated;
return m_stats;
}
return true;
template<typename T, typename U, int ch>
void calcStats(const T *data, size_t n, RawImage::Stats &stats)
{
U sum[4] = {0};
U sumSq[4] = {0};
T min[4] = {std::numeric_limits<T>::max(), std::numeric_limits<T>::max(), std::numeric_limits<T>::max(), std::numeric_limits<T>::max()};
T max[4] = {std::numeric_limits<T>::min(), std::numeric_limits<T>::min(), std::numeric_limits<T>::min(), std::numeric_limits<T>::min()};
uint32_t histSize = 65536;
if constexpr(std::is_same<T, uint8_t>::value)histSize = 256;
uint32_t histogram[4][65536] = {};
T sat = SATURATION * std::numeric_limits<T>::max();
if constexpr(!std::numeric_limits<T>::is_integer)sat = SATURATION;
uint32_t saturated[4] = {0};
auto statsFunc = [&](T d, int x)
{
sum[x] += d;
sumSq[x] += (U)d * d;
min[x] = std::min(min[x], d);
max[x] = std::max(max[x], d);
uint16_t idx;
if constexpr(std::is_same<T, uint32_t>::value)idx = d >> 16;
if constexpr(std::is_same<T, uint8_t>::value || std::is_same<T, uint16_t>::value)idx = d;
if constexpr(!std::numeric_limits<T>::is_integer)idx = std::clamp((T)d * histSize, (T)0.0, (T)65535.0);
histogram[x][idx]++;
if(d > sat)saturated[x]++;
};
auto findMedian = [n, histSize](uint32_t histogram[]) -> size_t
{
size_t histSum = 0;
for(size_t o=0; o < histSize; o++)
{
histSum += histogram[o];
if(histSum >= n/2)
return o;
}
return 0;
};
for(size_t i = 0; i < n; i++)
{
statsFunc(data[i*ch], 0);
if constexpr(ch == 4)
{
statsFunc(data[i*ch + 1], 1);
statsFunc(data[i*ch + 2], 2);
}
}
for(int i = 0; i < 3; i++)
{
stats.m_min[i] = min[i];
stats.m_max[i] = max[i];
stats.m_mean[i] = (double)sum[i] / n;
stats.m_saturated[i] = saturated[i];
double sum2 = (double)sum[i] * sum[i];
stats.m_stdDev[i] = std::sqrt((sumSq[i] - sum2 / n) / (n - 1));
uint32_t median = findMedian(histogram[0]);
stats.m_median[i] = median;
uint32_t madHist[65536] = {0};
madHist[0] = histogram[i][median];
for(size_t o = 1; o < histSize; o++)
{
if(median + o < histSize)madHist[o] += histogram[i][median + o];
if(o <= median)madHist[o] += histogram[i][median - o];
}
stats.m_mad[i] = findMedian(madHist);
if constexpr(!std::numeric_limits<T>::is_integer)
{
stats.m_median[i] /= 65535.0;
stats.m_mad[i] /= 65535.0;
}
}
}
void RawImage::calcStats()
@@ -134,60 +204,39 @@ void RawImage::calcStats()
if(m_stats.m_stats)return;
m_stats.m_stats = true;
/*cv::Scalar meanS, stdDevS;
cv::meanStdDev(m_img, meanS, stdDevS);
cv::minMaxIdx(m_img, &m_min, &m_max);
cv::Mat img;
if(m_img.channels() == 1)img = m_img;
else if (m_img.channels() == 3)cv::cvtColor(m_img, img, cv::COLOR_BGR2GRAY);
else if (m_img.channels() == 4)cv::cvtColor(m_img, img, cv::COLOR_BGRA2GRAY);
int histSize = 256;
if(img.type() == CV_16U || img.type() == CV_32F)histSize = 65536;
float range[] = {0, (float)histSize};
if(img.type() == CV_32F)range[1] = 1.0f;
const float *ranges[] = {range};
cv::Mat hist;
cv::calcHist(&img, 1, nullptr, cv::Mat(), hist, 1, &histSize, ranges);
m_mean = meanS[0];
m_stdDev = stdDevS[0];
size_t halfImageSize = size()/2;
size_t medianSum = 0;
for(int i=0; i < histSize; i++)
switch(m_origType)
{
medianSum += hist.at<float>(0, i);
if(medianSum >= halfImageSize)
{
m_median = i;
break;
}
case UINT8:
if(channels()==1)
::calcStats<uint8_t, uint64_t, 1>(static_cast<const uint8_t*>(origData()), size(), m_stats);
else
::calcStats<uint8_t, uint64_t, 4>(static_cast<const uint8_t*>(origData()), size(), m_stats);
break;
case UINT16:
if(channels()==1)
::calcStats<uint16_t, uint64_t, 1>(static_cast<const uint16_t*>(origData()), size(), m_stats);
else
::calcStats<uint16_t, uint64_t, 4>(static_cast<const uint16_t*>(origData()), size(), m_stats);
break;
case UINT32:
if(channels()==1)
::calcStats<uint32_t, double, 1>(static_cast<const uint32_t*>(origData()), size(), m_stats);
else
::calcStats<uint32_t, double, 4>(static_cast<const uint32_t*>(origData()), size(), m_stats);
break;
case FLOAT32:
if(channels()==1)
::calcStats<float, double, 1>(static_cast<const float*>(origData()), size(), m_stats);
else
::calcStats<float, double, 4>(static_cast<const float*>(origData()), size(), m_stats);
break;
case FLOAT64:
if(channels()==1)
::calcStats<double, double, 1>(static_cast<const double*>(origData()), size(), m_stats);
else
::calcStats<double, double, 4>(static_cast<const double*>(origData()), size(), m_stats);
break;
}
if(img.type() == CV_32F)m_median /= histSize;
int threshold = SATURATION * histSize;
m_saturated = 0;
for(int i = histSize-1; i >= threshold; i--)
m_saturated += hist.at<float>(0, i);
cv::Mat absDev;
img.convertTo(absDev, CV_32F, 1, -m_median);
absDev = cv::abs(absDev);
cv::Mat madHist;
medianSum = 0;
cv::calcHist(&absDev, 1, nullptr, cv::Mat(), madHist, 1, &histSize, ranges);
for(int i=0; i < histSize; i++)
{
medianSum += madHist.at<float>(0, i);
if(medianSum >= halfImageSize)
{
m_mad = i;
break;
}
}
if(img.type() == CV_32F)m_mad /= histSize;*/
}
void RawImage::rect(int &x, int &y, int w, int h, std::vector<double> &r) const
@@ -290,7 +339,15 @@ const void *RawImage::data(uint32_t row, uint32_t col) const
return m_pixels.get() + (m_width * row * m_ch + col * m_ch) * typeSize(m_type);
}
void *RawImage::origData(uint32_t row, uint32_t col) const
const void *RawImage::origData() const
{
if(m_original)
return m_original.get();
else
return m_pixels.get();
}
const void *RawImage::origData(uint32_t row, uint32_t col) const
{
if(m_original)
return m_original.get() + (m_width * row * m_ch + col * m_ch) * typeSize(m_origType);
@@ -520,22 +577,49 @@ std::shared_ptr<RawImage> RawImage::fromPlanar(const void *pixels, uint32_t w, u
switch(type)
{
case UINT8:
#ifdef __SSE2__
if(ch==3)
fromPlanarSSE<uint8_t, 3>(pixels, image->data(), size);
else
fromPlanarSSE<uint8_t, 4>(pixels, image->data(), size);
#else
convert(static_cast<const uint8_t*>(pixels), static_cast<uint8_t*>(image->data()), UINT8_MAX);
#endif
break;
case UINT16:
#ifdef __SSE2__
if(ch==3)
fromPlanarSSE<uint16_t, 3>(pixels, static_cast<uint16_t*>(image->data()), size);
else
fromPlanarSSE<uint16_t, 4>(pixels, static_cast<uint16_t*>(image->data()), size);
#else
convert(static_cast<const uint16_t*>(pixels), static_cast<uint16_t*>(image->data()), UINT16_MAX);
#endif
break;
case UINT32:
#ifdef __SSE2__
if(ch==3)
fromPlanarSSE<uint32_t, 3>(pixels, image->data(), size);
else
fromPlanarSSE<uint32_t, 4>(pixels, image->data(), size);
#else
convert(static_cast<const uint32_t*>(pixels), static_cast<uint32_t*>(image->data()), UINT32_MAX);
#endif
break;
case FLOAT32:
#ifdef __SSE2__
if(ch==3)
fromPlanarSSE<float, 3>(pixels, image->data(), size);
else
fromPlanarSSE<float, 4>(pixels, image->data(), size);
#else
convert(static_cast<const float*>(pixels), static_cast<float*>(image->data()), 1);
#endif
break;
case FLOAT64:
convert(static_cast<const double*>(pixels), static_cast<double*>(image->data()), 1);
break;
}
return image;
}
@@ -546,7 +630,32 @@ std::vector<RawImage> RawImage::split() const
for(size_t i=0; i<m_channels; i++)
planes[i].allocate(m_width, m_height, 1, m_type);
size_t s = size();
auto extract = [&](auto *in, auto *out, size_t off)
{
for(size_t i=0; i < s; i+=m_ch)
out[i] = in[i*m_ch + off];
};
for(uint32_t i=0; i<m_ch; i++)
{
switch(m_type)
{
case UINT8:
extract(static_cast<const uint8_t*>(data()), static_cast<uint8_t*>(planes[i].data()), i);
break;
case UINT16:
extract(static_cast<const uint16_t*>(data()), static_cast<uint16_t*>(planes[i].data()), i);
break;
case UINT32:
case FLOAT32:
extract(static_cast<const uint32_t*>(data()), static_cast<uint32_t*>(planes[i].data()), i);
break;
case FLOAT64:
extract(static_cast<const double*>(data()), static_cast<double*>(planes[i].data()), i);
break;
}
}
return planes;
}