793 lines
22 KiB
C++
793 lines
22 KiB
C++
#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;
|
|
bool QUALITY_RESIZE = true;
|
|
|
|
#ifdef __SSE2__
|
|
template<typename T, int ch>
|
|
void fromPlanarSSE(const void *in, void *out, size_t count);
|
|
#endif
|
|
|
|
size_t RawImage::typeSize(RawImage::DataType type)
|
|
{
|
|
switch(type)
|
|
{
|
|
case RawImage::UINT8:
|
|
return 1;
|
|
case RawImage::UINT16:
|
|
return 2;
|
|
case RawImage::UINT32:
|
|
case RawImage::FLOAT32:
|
|
return 4;
|
|
case RawImage::FLOAT64:
|
|
return 8;
|
|
default: return 1;
|
|
}
|
|
}
|
|
|
|
void RawImage::allocate(uint32_t w, uint32_t h, uint32_t ch, DataType type)
|
|
{
|
|
m_width = w;
|
|
m_height = h;
|
|
m_channels = ch;
|
|
m_ch = ch == 3 ? 4 : ch;
|
|
m_origType = m_type = type;
|
|
m_pixels = std::make_unique<PixelType[]>(m_width * m_height * m_ch * typeSize(type));
|
|
}
|
|
|
|
RawImage::RawImage()
|
|
{
|
|
}
|
|
|
|
RawImage::RawImage(uint32_t w, uint32_t h, uint32_t ch, DataType type)
|
|
{
|
|
allocate(w, h, ch, type);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
RawImage::RawImage(RawImage &&d)
|
|
{
|
|
m_pixels = std::move(d.m_pixels);
|
|
m_original = std::move(d.m_original);
|
|
m_width = d.m_width;
|
|
m_height = d.m_height;
|
|
m_channels = d.m_channels;
|
|
m_ch = d.m_ch;
|
|
m_type = d.m_type;
|
|
m_origType = d.m_origType;
|
|
m_stats = d.m_stats;
|
|
m_thumbAspect = d.m_thumbAspect;
|
|
}
|
|
|
|
RawImage::RawImage(const QImage &img)
|
|
{
|
|
qDebug() << img;
|
|
if(img.format() == QImage::Format_RGBX8888)
|
|
{
|
|
allocate(img.width(), img.height(), 3, UINT8);
|
|
for(int i=0; i<img.height(); i++)
|
|
std::memcpy(data(i), img.scanLine(i), img.width()*4);
|
|
}
|
|
else if(img.format() == QImage::Format_RGBA8888)
|
|
{
|
|
allocate(img.width(), img.height(), 4, UINT8);
|
|
for(int i=0; i<img.height(); i++)
|
|
std::memcpy(data(i), img.scanLine(i), img.width()*4);
|
|
}
|
|
else if(img.format() == QImage::Format_RGBX64)
|
|
{
|
|
allocate(img.width(), img.height(), 3, UINT16);
|
|
for(int i=0; i<img.height(); i++)
|
|
std::memcpy(data(i), img.scanLine(i), img.width()*8);
|
|
}
|
|
else if(img.format() == QImage::Format_RGBA64)
|
|
{
|
|
allocate(img.width(), img.height(), 4, UINT16);
|
|
for(int i=0; i<img.height(); i++)
|
|
std::memcpy(data(i), img.scanLine(i), img.width()*8);
|
|
}
|
|
else if(img.format() == QImage::Format_Grayscale8)
|
|
{
|
|
allocate(img.width(), img.height(), 1, UINT8);
|
|
for(int i=0; i<img.height(); i++)
|
|
std::memcpy(data(i), img.scanLine(i), img.width());
|
|
}
|
|
else if(img.format() == QImage::Format_Grayscale16)
|
|
{
|
|
allocate(img.width(), img.height(), 1, UINT16);
|
|
for(int i=0; i<img.height(); i++)
|
|
std::memcpy(data(i), img.scanLine(i), img.width()*2);
|
|
}
|
|
else
|
|
{
|
|
QImage tmp = img.convertToFormat(QImage::Format_RGBA8888);
|
|
allocate(img.width(), img.height(), 4, UINT8);
|
|
for(int i=0; i<tmp.height(); i++)
|
|
std::memcpy(data(i), tmp.scanLine(i), tmp.width()*4);
|
|
}
|
|
m_stats.m_stats = false;
|
|
}
|
|
|
|
const RawImage::Stats& RawImage::imageStats() const
|
|
{
|
|
return m_stats;
|
|
}
|
|
|
|
template<typename T, typename U, int ch>
|
|
void calcStats(const T *data, size_t n, size_t w, 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;
|
|
std::vector<uint32_t> histogram[4];
|
|
histogram[0].resize(histSize); histogram[1].resize(histSize); histogram[2].resize(histSize); histogram[3].resize(histSize);
|
|
|
|
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 = [histSize](std::vector<uint32_t> &histogram, size_t n) -> 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;
|
|
};
|
|
|
|
size_t na[4] = {n, n, n, n};
|
|
if constexpr(ch == 1)
|
|
{
|
|
na[1] /= 4;
|
|
na[2] /= 2;
|
|
na[3] /= 4;
|
|
}
|
|
for(size_t i = 0; i < n; i++)
|
|
{
|
|
statsFunc(data[i*ch], 0);
|
|
if constexpr(ch >= 3)
|
|
{
|
|
statsFunc(data[i*ch + 1], 1);
|
|
statsFunc(data[i*ch + 2], 2);
|
|
}
|
|
}
|
|
if constexpr(ch == 1)
|
|
{
|
|
size_t h = (n / w) & (SIZE_MAX-1);
|
|
w &= (SIZE_MAX-1);
|
|
for(size_t y=0; y<h; y+=2)
|
|
{
|
|
for(size_t x=0; x<w; x+=2)
|
|
{
|
|
statsFunc(data[y*w+x], 1);
|
|
statsFunc(data[y*w+x+1], 2);
|
|
statsFunc(data[(y+1)*w+x], 2);
|
|
statsFunc(data[(y+1)*w+x+1], 3);
|
|
}
|
|
}
|
|
}
|
|
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
stats.m_min[i] = min[i];
|
|
stats.m_max[i] = max[i];
|
|
stats.m_mean[i] = (double)sum[i] / na[i];
|
|
stats.m_saturated[i] = saturated[i];
|
|
double sum2 = (double)sum[i] * sum[i];
|
|
stats.m_stdDev[i] = std::sqrt((sumSq[i] - sum2 / na[i]) / (na[i] - 1));
|
|
|
|
uint32_t median = findMedian(histogram[i], na[i]);
|
|
stats.m_median[i] = median;
|
|
std::vector<uint32_t> madHist(histSize, 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, na[i]);
|
|
if constexpr(!std::numeric_limits<T>::is_integer)
|
|
{
|
|
stats.m_median[i] /= 65535.0;
|
|
stats.m_mad[i] /= 65535.0;
|
|
}
|
|
}
|
|
|
|
stats.m_histogram.resize(histSize, 0);
|
|
for(size_t i = 0; i < histSize; i++)
|
|
for(size_t o = 0; o < ch; o++)
|
|
stats.m_histogram[i] += histogram[o][i];
|
|
}
|
|
|
|
void RawImage::calcStats()
|
|
{
|
|
if(m_stats.m_stats)return;
|
|
m_stats.m_stats = true;
|
|
|
|
switch(m_origType)
|
|
{
|
|
case UINT8:
|
|
if(channels()==1)
|
|
::calcStats<uint8_t, uint64_t, 1>(static_cast<const uint8_t*>(origData()), size(), width(), m_stats);
|
|
else
|
|
::calcStats<uint8_t, uint64_t, 4>(static_cast<const uint8_t*>(origData()), size(), width(), m_stats);
|
|
break;
|
|
case UINT16:
|
|
if(channels()==1)
|
|
::calcStats<uint16_t, uint64_t, 1>(static_cast<const uint16_t*>(origData()), size(), width(), m_stats);
|
|
else
|
|
::calcStats<uint16_t, uint64_t, 4>(static_cast<const uint16_t*>(origData()), size(), width(), m_stats);
|
|
break;
|
|
case UINT32:
|
|
if(channels()==1)
|
|
::calcStats<uint32_t, double, 1>(static_cast<const uint32_t*>(origData()), size(), width(), m_stats);
|
|
else
|
|
::calcStats<uint32_t, double, 4>(static_cast<const uint32_t*>(origData()), size(), width(), m_stats);
|
|
break;
|
|
case FLOAT32:
|
|
if(channels()==1)
|
|
::calcStats<float, double, 1>(static_cast<const float*>(origData()), size(), width(), m_stats);
|
|
else
|
|
::calcStats<float, double, 4>(static_cast<const float*>(origData()), size(), width(), m_stats);
|
|
break;
|
|
case FLOAT64:
|
|
if(channels()==1)
|
|
::calcStats<double, double, 1>(static_cast<const double*>(origData()), size(), width(), m_stats);
|
|
else
|
|
::calcStats<double, double, 4>(static_cast<const double*>(origData()), size(), width(), m_stats);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void RawImage::rect(int &x, int &y, int w, int h, std::vector<double> &r) const
|
|
{
|
|
/*r.resize(w*h);
|
|
x -= w/2;
|
|
y -= h/2;
|
|
if(x<0)x = 0;
|
|
if(y<0)y = 0;
|
|
if(x+w >= m_img.cols)x = m_img.cols-w;
|
|
if(y+h >= m_img.rows)y = m_img.rows-h;
|
|
cv::Mat roiImg(m_img, cv::Rect(x, y, w, h));
|
|
cv::Mat doubleMat;
|
|
roiImg.convertTo(doubleMat, CV_64F);
|
|
r = std::vector<double>(doubleMat.begin<double>(), doubleMat.end<double>());*/
|
|
}
|
|
|
|
int RawImage::findPeaks(double background, double distance, std::vector<Peak> &peaks) const
|
|
{
|
|
/*std::vector<std::vector<cv::Point>> contours;
|
|
|
|
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(distance, distance));
|
|
|
|
cv::Mat img, mask, dilate, locMax, result;
|
|
if(m_img.channels() == 1)img = m_img;
|
|
else cv::cvtColor(m_img, img, cv::COLOR_RGB2GRAY);
|
|
|
|
cv::dilate(img, dilate, kernel);
|
|
cv::compare(img, dilate, locMax, cv::CMP_GE);
|
|
cv::compare(img, cv::Scalar(background), mask, cv::CMP_GT);
|
|
cv::bitwise_and(locMax, mask, result);
|
|
|
|
cv::findContours(result, contours, cv::noArray(), cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
|
|
peaks.reserve(contours.size());
|
|
for(auto contour : contours)
|
|
{
|
|
peaks.push_back(Peak(1, contour[0].x, contour[0].y));
|
|
}
|
|
|
|
return peaks.size();*/
|
|
}
|
|
|
|
uint32_t RawImage::width() const
|
|
{
|
|
return m_width;
|
|
}
|
|
|
|
uint32_t RawImage::height() const
|
|
{
|
|
return m_height;
|
|
}
|
|
|
|
uint32_t RawImage::channels() const
|
|
{
|
|
return m_channels;
|
|
}
|
|
|
|
uint32_t RawImage::size() const
|
|
{
|
|
return width()*height();
|
|
}
|
|
|
|
RawImage::DataType RawImage::type() const
|
|
{
|
|
return m_type;
|
|
}
|
|
|
|
uint32_t RawImage::norm() const
|
|
{
|
|
switch(m_type)
|
|
{
|
|
case UINT8:
|
|
return UINT8_MAX;
|
|
case UINT16:
|
|
return UINT16_MAX;
|
|
case UINT32:
|
|
return UINT32_MAX;
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
uint32_t RawImage::widthBytes() const
|
|
{
|
|
return m_ch * m_width;
|
|
}
|
|
|
|
void* RawImage::data()
|
|
{
|
|
return m_pixels.get();
|
|
}
|
|
|
|
const void *RawImage::data() const
|
|
{
|
|
return m_pixels.get();
|
|
}
|
|
|
|
void *RawImage::data(uint32_t row, uint32_t col)
|
|
{
|
|
return m_pixels.get() + (m_width * row * m_ch + col * m_ch) * typeSize(m_type);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
else
|
|
return m_pixels.get() + (m_width * row * m_ch + col * m_ch) * typeSize(m_type);
|
|
}
|
|
|
|
bool RawImage::planar() const
|
|
{
|
|
return m_planar;
|
|
}
|
|
|
|
void RawImage::convertToThumbnail()
|
|
{
|
|
if(!valid())
|
|
return;
|
|
|
|
if(m_thumbAspect == 0.0f)
|
|
m_thumbAspect = (float)width() / height();
|
|
std::unique_ptr<PixelType[]> outptr = std::make_unique<PixelType[]>(THUMB_SIZE * THUMB_SIZE * 4 * sizeof(uint16_t));
|
|
uint16_t *out = reinterpret_cast<uint16_t*>(outptr.get());
|
|
|
|
auto loop = [&](uint16_t *out, auto *in, auto scale)
|
|
{
|
|
for(int i=0; i<THUMB_SIZE; i++)
|
|
{
|
|
for(int o=0; o<THUMB_SIZE; o++)
|
|
{
|
|
int idx = (i*THUMB_SIZE + o)*4;
|
|
int idx2 = ((i * m_height / THUMB_SIZE * m_width) + (o * m_width / THUMB_SIZE)) * m_ch;
|
|
|
|
if(m_channels == 1)
|
|
{
|
|
out[idx] = out[idx + 1] = out[idx + 2] = in[idx2] * scale;
|
|
}
|
|
else
|
|
{
|
|
out[idx] = in[idx2] * scale;;
|
|
out[idx + 1] = in[idx2 + 1] * scale;;
|
|
out[idx + 2] = in[idx2 + 2] * scale;;
|
|
}
|
|
out[idx + 3] = UINT16_MAX;
|
|
}
|
|
}
|
|
};
|
|
|
|
switch(m_type)
|
|
{
|
|
case UINT8:
|
|
loop(out, reinterpret_cast<uint8_t*>(m_pixels.get()), 256);
|
|
break;
|
|
case UINT16:
|
|
loop(out, reinterpret_cast<uint16_t*>(m_pixels.get()), 1);
|
|
break;
|
|
case UINT32:
|
|
loop(out, reinterpret_cast<uint32_t*>(m_pixels.get()), UINT16_MAX/(float)UINT32_MAX);
|
|
break;
|
|
case FLOAT32:
|
|
loop(out, reinterpret_cast<float*>(m_pixels.get()), 65535.0);
|
|
break;
|
|
default:
|
|
qWarning() << "FLOAT64 should not happend";
|
|
return;
|
|
}
|
|
|
|
m_pixels = std::move(outptr);
|
|
m_width = THUMB_SIZE;
|
|
m_height = THUMB_SIZE;
|
|
m_ch = 4;
|
|
m_channels = 3;
|
|
m_type = UINT16;
|
|
}
|
|
|
|
void RawImage::convertToGLFormat()
|
|
{
|
|
size_t s = size() * m_ch;
|
|
if(m_type == UINT32)
|
|
{
|
|
m_original = std::move(m_pixels);
|
|
allocate(m_width, m_height, m_channels, FLOAT32);
|
|
m_origType = UINT32;
|
|
float *dst = reinterpret_cast<float*>(m_pixels.get());
|
|
uint32_t *src = reinterpret_cast<uint32_t*>(m_original.get());
|
|
|
|
for(size_t i = 0; i < s; i++)
|
|
dst[i] = src[i] / (float)UINT32_MAX;
|
|
}
|
|
else if(m_type == FLOAT64)
|
|
{
|
|
m_original = std::move(m_pixels);
|
|
allocate(m_width, m_height, m_channels, FLOAT32);
|
|
m_origType = FLOAT64;
|
|
float *dst = reinterpret_cast<float*>(m_pixels.get());
|
|
double *src = reinterpret_cast<double*>(m_original.get());
|
|
|
|
for(size_t i = 0; i < s; i++)
|
|
dst[i] = src[i];
|
|
}
|
|
}
|
|
|
|
float RawImage::thumbAspect() const
|
|
{
|
|
return m_thumbAspect;
|
|
}
|
|
|
|
bool RawImage::pixel(int x, int y, double &r, double &g, double &b) const
|
|
{
|
|
if(x < 0 || y < 0 || x >= (int)width() || y >= (int)height())return false;
|
|
|
|
switch(m_origType)
|
|
{
|
|
case UINT8:
|
|
{
|
|
const uint8_t *v = static_cast<const uint8_t*>(origData(y, x));
|
|
if(m_channels == 1)
|
|
{
|
|
r = g = b = *v;
|
|
}
|
|
else
|
|
{
|
|
r = v[0];
|
|
g = v[1];
|
|
b = v[2];
|
|
}
|
|
break;
|
|
}
|
|
case UINT16:
|
|
{
|
|
const uint16_t *v = static_cast<const uint16_t*>(origData(y, x));
|
|
if(m_channels == 1)
|
|
{
|
|
r = g = b = *v;
|
|
}
|
|
else
|
|
{
|
|
r = v[0];
|
|
g = v[1];
|
|
b = v[2];
|
|
}
|
|
break;
|
|
}
|
|
case UINT32:
|
|
{
|
|
const uint32_t *v = static_cast<const uint32_t*>(origData(y, x));
|
|
if(m_channels == 1)
|
|
{
|
|
r = g = b = *v;
|
|
}
|
|
else
|
|
{
|
|
r = v[0];
|
|
g = v[1];
|
|
b = v[2];
|
|
}
|
|
break;
|
|
}
|
|
case FLOAT32:
|
|
{
|
|
const float *v = static_cast<const float*>(origData(y, x));
|
|
if(m_channels == 1)
|
|
{
|
|
r = g = b = *v;
|
|
}
|
|
else
|
|
{
|
|
r = v[0];
|
|
g = v[1];
|
|
b = v[2];
|
|
}
|
|
break;
|
|
}
|
|
case FLOAT64:
|
|
{
|
|
const double *v = static_cast<const double*>(origData(y, x));
|
|
if(m_channels == 1)
|
|
{
|
|
r = g = b = *v;
|
|
}
|
|
else
|
|
{
|
|
r = v[0];
|
|
g = v[1];
|
|
b = v[2];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template<typename T>
|
|
void boxResample(uint32_t w, uint32_t h, uint32_t ch, uint32_t oldw, uint32_t oldh, const uint8_t *in_, uint8_t *out_)
|
|
{
|
|
if(oldw == 0 || oldh == 0)return;
|
|
|
|
const T *in = reinterpret_cast<const T*>(in_);
|
|
T *out = reinterpret_cast<T*>(out_);
|
|
float max = 255.0f;
|
|
if constexpr(std::is_same<T, uint16_t>::value)
|
|
max = UINT16_MAX;
|
|
|
|
float sx = (float)w / oldw;
|
|
float sy = (float)h / oldh;
|
|
for(uint32_t y = 0; y < h; y++)//iterate over destination Y
|
|
{
|
|
for(uint32_t x = 0; x < w; x++)//iterate over destination X
|
|
{
|
|
float p[4] = {0.0f};
|
|
uint32_t xx = x * oldw / w;//calculate source rect
|
|
uint32_t yy = y * oldh / h;
|
|
uint32_t xe = std::min((x + 1) * oldw / w, oldw - 1);
|
|
uint32_t ye = std::min((y + 1) * oldh / h, oldh - 1);
|
|
for(uint32_t o = yy; o <= ye; o++)//iterate over source Y
|
|
{
|
|
float cy = o * sy - y;
|
|
if(cy < 0.0f)cy = sy + cy;
|
|
else if(sy + cy > 1.0f)cy = 1.0f - cy;
|
|
else cy = sy;
|
|
if(yy==ye)cy = 1.0f;
|
|
for(uint32_t i = xx; i <= xe; i++)//iterate over source X
|
|
{
|
|
float cx = i * sx - x;
|
|
if(cx < 0.0f)cx = sx + cx;
|
|
else if(sx + cx > 1.0f)cx = 1.0f - cx;
|
|
else cx = sx;
|
|
if(xx==xe)cx = 1.0f;
|
|
for(uint32_t z = 0; z < ch; z++)
|
|
p[z] += in[(o * oldw + i) * ch + z] * cy * cx;
|
|
}
|
|
}
|
|
for(uint32_t z = 0; z < ch; z++)
|
|
if constexpr(std::is_floating_point<T>::value)
|
|
out[(y * w + x) * ch + z] = p[z];
|
|
else
|
|
out[(y * w + x) * ch + z] = std::clamp(std::round(p[z]), 0.0f, max);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RawImage::resize(uint32_t w, uint32_t h)
|
|
{
|
|
if(!valid())return;
|
|
|
|
std::unique_ptr<PixelType[]> old_pixels = std::move(m_pixels);
|
|
uint32_t oldw = m_width;
|
|
uint32_t oldh = m_height;
|
|
m_thumbAspect = (float)m_width / m_height;
|
|
|
|
allocate(w, h, m_channels, m_type);
|
|
|
|
switch(m_type)
|
|
{
|
|
case RawImage::UINT8:
|
|
boxResample<uint8_t>(w, h, m_ch, oldw, oldh, old_pixels.get(), m_pixels.get());
|
|
break;
|
|
case RawImage::UINT16:
|
|
boxResample<uint16_t>(w, h, m_ch, oldw, oldh, old_pixels.get(), m_pixels.get());
|
|
break;
|
|
case RawImage::FLOAT32:
|
|
boxResample<float>(w, h, m_ch, oldw, oldh, old_pixels.get(), m_pixels.get());
|
|
break;
|
|
default:
|
|
qWarning() << "Resizing format not supported";
|
|
break;
|
|
}
|
|
}
|
|
|
|
std::pair<float, float> RawImage::unitScale() const
|
|
{
|
|
float min = *std::min_element(m_stats.m_min, m_stats.m_min + 4);
|
|
float max = *std::max_element(m_stats.m_max, m_stats.m_max + 4);
|
|
|
|
if(m_origType == UINT32)
|
|
{
|
|
min /= (float)UINT32_MAX;
|
|
max /= (float)UINT32_MAX;
|
|
}
|
|
|
|
if(min < 0.0f || max > 1.0f)
|
|
return {1.0f / (max - min), min / (max - min)};
|
|
else
|
|
return {1.0f, 0.0f};
|
|
}
|
|
|
|
void RawImage::flip()
|
|
{
|
|
std::unique_ptr<PixelType[]> tmp = std::move(m_pixels);
|
|
allocate(m_width, m_height, m_channels, m_type);
|
|
uint32_t rowSize = m_width * m_ch * typeSize(m_type);
|
|
for(uint32_t i=0; i<m_height; i++)
|
|
std::memcpy(m_pixels.get() + rowSize * (m_height - i - 1), tmp.get() + rowSize * i, rowSize);
|
|
}
|
|
|
|
std::shared_ptr<RawImage> RawImage::fromPlanar(const RawImage &img)
|
|
{
|
|
return RawImage::fromPlanar(img.data(), img.width(), img.height(), img.channels(), img.type());
|
|
}
|
|
|
|
std::shared_ptr<RawImage> RawImage::fromPlanar(const void *pixels, uint32_t w, uint32_t h, uint32_t ch, RawImage::DataType type)
|
|
{
|
|
std::shared_ptr<RawImage> image = std::make_shared<RawImage>(w, h, ch, type);
|
|
size_t size = w * h;
|
|
size_t ch2 = ch == 1 ? 1 : 4;
|
|
auto convert = [&](auto *in, auto *out, auto alpha)
|
|
{
|
|
for(size_t i=0; i<size; i++)
|
|
for(size_t o=0; o<ch; o++)
|
|
out[i*ch2 + o] = in[o*size + i];
|
|
|
|
if(ch != ch2)
|
|
for(size_t i=0; i<size; i++)
|
|
out[i*ch2 + 3] = alpha;
|
|
};
|
|
|
|
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;
|
|
}
|
|
image->m_planar = false;
|
|
return image;
|
|
}
|
|
|
|
std::vector<RawImage> RawImage::split() const
|
|
{
|
|
std::vector<RawImage> planes;
|
|
planes.resize(m_channels);
|
|
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++)
|
|
out[i] = in[i*m_ch + off];
|
|
};
|
|
|
|
for(uint32_t i=0; i<m_channels; 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;
|
|
}
|
|
|
|
bool RawImage::valid() const
|
|
{
|
|
return m_width > 0 && m_height > 0;
|
|
}
|