diff --git a/CMakeLists.txt b/CMakeLists.txt index ac9fa19..6fd1663 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,6 @@ set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s") find_package(Qt5 COMPONENTS Widgets Sql OpenGL REQUIRED) -find_package(OpenCV REQUIRED) find_library(GSL_LIB gsl REQUIRED) find_library(GSLCBLAS_LIB gslcblas REQUIRED) find_library(EXIF_LIB exif REQUIRED) @@ -70,13 +69,13 @@ endif() add_executable(tenmon WIN32 MACOSX_BUNDLE ${tenmon_ICON} ${TENMON_SRC}) find_path(FITS_INCLUDE fitsio2.h PATH_SUFFIXES cfitsio REQUIRED) -target_include_directories(tenmon PRIVATE ${OpenCV_INCLUDE_DIRS} ${FITS_INCLUDE} ${CMAKE_BINARY_DIR} ${libXISF_SOURCE_DIR}) +target_include_directories(tenmon PRIVATE ${FITS_INCLUDE} ${CMAKE_BINARY_DIR} ${libXISF_SOURCE_DIR}) if(UNIX AND NOT APPLE) target_include_directories(tenmon PRIVATE ${GIO_INCLUDE_DIRS}) endif() -target_link_libraries(tenmon Qt5::Widgets Qt5::Sql ${OpenCV_LIBS} ${GSL_LIB} ${GSLCBLAS_LIB} ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB} XISF) +target_link_libraries(tenmon Qt5::Widgets Qt5::Sql ${GSL_LIB} ${GSLCBLAS_LIB} ${EXIF_LIB} ${FITS_LIB} ${RAW_LIB} ${WCS_LIB} XISF) if(APPLE) target_link_libraries(tenmon "-framework CoreFoundation") else() diff --git a/imagescrollareagl.cpp b/imagescrollareagl.cpp index d03b2f8..59eb851 100644 --- a/imagescrollareagl.cpp +++ b/imagescrollareagl.cpp @@ -12,47 +12,48 @@ #include #include #include +#include struct RawImageType { QOpenGLTexture::PixelFormat pixelFormat; QOpenGLTexture::TextureFormat textureFormat; QOpenGLTexture::PixelType dataType; - bool bw; }; -const RawImageType rawImageTypes[] = { - {QOpenGLTexture::Red, QOpenGLTexture::R8_UNorm, QOpenGLTexture::UInt8, true}, - {QOpenGLTexture::Red, QOpenGLTexture::R16_UNorm, QOpenGLTexture::UInt16, true}, - {QOpenGLTexture::Red, QOpenGLTexture::R32F, QOpenGLTexture::Float32, true}, -#ifdef COLOR_MANAGMENT - {QOpenGLTexture::RGB, QOpenGLTexture::SRGB8, QOpenGLTexture::UInt8, false}, - {QOpenGLTexture::RGBA,QOpenGLTexture::SRGB8_Alpha8, QOpenGLTexture::UInt8, false}, -#else - {QOpenGLTexture::RGB, QOpenGLTexture::RGB8_UNorm, QOpenGLTexture::UInt8, false}, - {QOpenGLTexture::RGBA,QOpenGLTexture::RGBA8_UNorm, QOpenGLTexture::UInt8, false}, -#endif - {QOpenGLTexture::RGB, QOpenGLTexture::RGB16_UNorm, QOpenGLTexture::UInt16, false}, - {QOpenGLTexture::RGBA, QOpenGLTexture::RGB16_UNorm, QOpenGLTexture::UInt16, false}, - {QOpenGLTexture::RGB, QOpenGLTexture::RGB32F, QOpenGLTexture::Float32, false} -}; - -static bool MANUAL_MIPMAP_GEN = false; - -void setScrollRange(QScrollBar *scrollBar, int newRange) +RawImageType getRawImageType(const RawImage *img) { - int page = scrollBar->pageStep(); - int pos = scrollBar->value() + page/2; - int range = scrollBar->maximum() + page; - float relPos = (float)pos/(float)range; + RawImageType type; + switch(img->type()) + { + case RawImage::UINT8: + if(img->channels() >= 3) + type.textureFormat = QOpenGLTexture::SRGB8_Alpha8; + else + type.textureFormat = QOpenGLTexture::R8_UNorm; + type.dataType = QOpenGLTexture::UInt8; + break; + case RawImage::UINT16: + if(img->channels() >= 3) + type.textureFormat = QOpenGLTexture::RGBA16_UNorm; + else + type.textureFormat = QOpenGLTexture::R16_UNorm; + type.dataType = QOpenGLTexture::UInt16; + break; + case RawImage::FLOAT32: + if(img->channels() >= 3) + type.textureFormat = QOpenGLTexture::RGBA32F; + else + type.textureFormat = QOpenGLTexture::R32F; + type.dataType = QOpenGLTexture::Float32; + } - if(page >= newRange) - scrollBar->hide(); + if(img->channels() >= 3) + type.pixelFormat = QOpenGLTexture::RGBA; else - scrollBar->show(); + type.pixelFormat = QOpenGLTexture::Red; - scrollBar->setRange(0, newRange - page); - scrollBar->setValue(relPos*newRange - page/2); + return type; } ImageWidget::ImageWidget(Database *database, QWidget *parent) : QOpenGLWidget(parent) @@ -108,10 +109,12 @@ void ImageWidget::setImage(std::shared_ptr image, int index) if(!m_image)return; - const RawImageType &rawImageType = rawImageTypes[image->type()]; - m_srgb = rawImageType.textureFormat == QOpenGLTexture::SRGB8 || rawImageType.textureFormat == QOpenGLTexture::SRGB8_Alpha8; - m_bwImg = rawImageType.bw; + RawImageType rawImageType = getRawImageType(image.get()); + m_srgb = rawImageType.textureFormat == QOpenGLTexture::SRGB8_Alpha8; + m_bwImg = image->channels() == 1; + QElapsedTimer timer; + timer.start(); m_image->destroy(); m_image->setAutoMipMapGenerationEnabled(false); m_image->setFormat(rawImageType.textureFormat); @@ -122,40 +125,33 @@ void ImageWidget::setImage(std::shared_ptr image, int index) m_image->setWrapMode(QOpenGLTexture::ClampToEdge); m_image->setBorderColor(0, 0, 0, 0); m_image->setData(0, rawImageType.pixelFormat, rawImageType.dataType, (const void*)image->data(), m_transferOptions.get()); + m_image->generateMipMaps(); + qDebug() << "setImage" << timer.elapsed(); - auto sRGB_linear = [](cv::Point3f &pixel, const int *pos) + /*QElapsedTimer timer; + RawImage xxx(8192, 8192, 4, RawImage::UINT32); + uint32_t *p = (uint32_t*)xxx.data(); + for(int i=0; i<8192*8192*4; i++) + p[i] = rand(); + auto test = [&](QOpenGLTexture::PixelFormat format) { - pixel.x = pixel.x <= 0.04045f ? pixel.x / 12.92f : std::pow((pixel.x + 0.055) / 1.055f, 2.4f); - pixel.y = pixel.y <= 0.04045f ? pixel.y / 12.92f : std::pow((pixel.y + 0.055) / 1.055f, 2.4f); - pixel.z = pixel.z <= 0.04045f ? pixel.z / 12.92f : std::pow((pixel.z + 0.055) / 1.055f, 2.4f); + timer.start(); + m_image->destroy(); + //m_image->setAutoMipMapGenerationEnabled(false); + m_image->setFormat(QOpenGLTexture::TextureFormat::RGBA8_UNorm); + m_image->setSize(8192, 8192); + //m_image->setMipLevels([&](){ int c = 0; int s = std::min(m_imgWidth, m_imgHeight); while(s>>=1)c++; return c; }()); + m_image->allocateStorage(); + m_image->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear); + m_image->setWrapMode(QOpenGLTexture::ClampToEdge); + m_image->setBorderColor(0, 0, 0, 0); + m_image->setData(0, format, QOpenGLTexture::UInt8, (const void*)p, m_transferOptions.get()); + qDebug() << format << timer.elapsed(); }; - - auto linear_sRGB = [](cv::Point3f &pixel, const int *pos) - { - pixel.x = pixel.x <= 0.0031308f ? pixel.x * 12.92f : 1.055f * std::pow(pixel.x , 1/2.4f) - 0.055f; - pixel.y = pixel.y <= 0.0031308f ? pixel.y * 12.92f : 1.055f * std::pow(pixel.y , 1/2.4f) - 0.055f; - pixel.z = pixel.z <= 0.0031308f ? pixel.z * 12.92f : 1.055f * std::pow(pixel.z , 1/2.4f) - 0.055f; - }; - - //AMD OpenGL driver on Windows doesn't generate mipmaps for sRGB textures correctly - if(m_srgb && MANUAL_MIPMAP_GEN) - { - cv::Mat img = image->mat(); - img.convertTo(img, CV_32FC3, 1/255.0); - img.forEach(sRGB_linear); - cv::Size size(img.cols, img.rows); - for(int i=1; imipLevels(); i++) - { - cv::Mat mip; - size /= 2; - cv::resize(img, mip, size); - mip.copyTo(img); - mip.forEach(linear_sRGB); - mip.convertTo(mip, CV_8UC3, 255); - m_image->setData(i, rawImageType.pixelFormat, rawImageType.dataType, (const void*)mip.ptr(), m_transferOptions.get()); - } - } - else m_image->generateMipMaps(); + test(QOpenGLTexture::PixelFormat::BGR); + test(QOpenGLTexture::PixelFormat::RGB); + test(QOpenGLTexture::PixelFormat::BGRA); + test(QOpenGLTexture::PixelFormat::RGBA);*/ if(m_debayerTex) { @@ -308,7 +304,8 @@ void ImageWidget::thumbnailLoaded(const Image *image) { makeCurrent(); const RawImage *raw = image->thumbnail(); - m_thumbnailTexture->setData(0, image->number(), QOpenGLTexture::RGB, QOpenGLTexture::UInt16, raw->data(), m_transferOptions.get()); + if(!raw)return; + m_thumbnailTexture->setData(0, image->number(), QOpenGLTexture::RGBA, QOpenGLTexture::UInt16, raw->data(), m_transferOptions.get()); float a = raw->thumbAspect(); int sizes[3] = { std::max(1, a > 1.0f ? THUMB_SIZE : (int)(THUMB_SIZE * a)), std::max(1, a < 1.0f ? THUMB_SIZE : (int)(THUMB_SIZE / a)), image->number() }; m_sizesDirty = true; @@ -612,7 +609,7 @@ void ImageWidget::mouseMoveEvent(QMouseEvent *event) if(!m_showThumbnails && m_rawImage) { QVector2D pix = getImagePixelCoord(QVector2D(event->pos())); - QVector3D rgb; + double r,g,b; SkyPoint sky; if(m_wcs) @@ -620,12 +617,12 @@ void ImageWidget::mouseMoveEvent(QMouseEvent *event) m_wcs->pixelToWorld(QPointF(pix.x(), pix.y()), sky); } - if(m_rawImage->pixel(pix.x(), pix.y(), rgb)) + if(m_rawImage->pixel(pix.x(), pix.y(), r, g, b)) { if(m_bwImg) - emit status(tr("L:%1").arg(rgb.x()), tr("X:%3 Y:%4").arg((int)pix.x()).arg((int)pix.y()), sky.toString()); + emit status(tr("L:%1").arg(r), tr("X:%3 Y:%4").arg((int)pix.x()).arg((int)pix.y()), sky.toString()); else - emit status(tr("R:%1 G:%2 B:%3").arg(rgb.x()).arg(rgb.y()).arg(rgb.z()), tr("X:%3 Y:%4").arg((int)pix.x()).arg((int)pix.y()), sky.toString()); + emit status(tr("R:%1 G:%2 B:%3").arg(r).arg(g).arg(b), tr("X:%3 Y:%4").arg((int)pix.x()).arg((int)pix.y()), sky.toString()); } } } diff --git a/loadrunable.cpp b/loadrunable.cpp index b19f0ce..16034e4 100644 --- a/loadrunable.cpp +++ b/loadrunable.cpp @@ -115,7 +115,7 @@ bool loadRAW(const QString path, ImageInfoData &info, RawImage **image) out[d++] = p; } } - *image = new RawImage(rawdata.sizes.width, rawdata.sizes.height, RawImage::UINT16); + *image = new RawImage(rawdata.sizes.width, rawdata.sizes.height, 1, RawImage::UINT16); memcpy((*image)->data(), &out[0], sizeof(uint16_t)*d); } @@ -212,28 +212,35 @@ bool loadFITS(const QString path, ImageInfoData &info, RawImage **image) if(naxis >= 2 && naxis <= 3 && status == 0) { - int cvtype; + RawImage::DataType type; int fitstype; - std::vector cvimg; long fpixel[3] = {1,1,1}; switch(imgtype) { case BYTE_IMG: - cvtype = CV_8U; + type = RawImage::UINT8; fitstype = TBYTE; break; case SHORT_IMG: - cvtype = CV_16S; + type = RawImage::UINT16; fitstype = TSHORT; break; case USHORT_IMG: - cvtype = CV_16U; + type = RawImage::UINT16; fitstype = TUSHORT; break; + case ULONG_IMG: + type = RawImage::UINT32; + fitstype = TUINT; + break; case FLOAT_IMG: - cvtype = CV_32F; + type = RawImage::FLOAT32; fitstype = TFLOAT; break; + case DOUBLE_IMG: + type = RawImage::FLOAT64; + fitstype = TDOUBLE; + break; default: info.info.append({QObject::tr("Error"), QObject::tr("Unsupported sample format")}); goto noload; @@ -247,26 +254,28 @@ bool loadFITS(const QString path, ImageInfoData &info, RawImage **image) info.info.append({QObject::tr("Width"), QString::number(naxes[0])}); info.info.append({QObject::tr("Height"), QString::number(naxes[1])}); + RawImage img(w, h, naxis == 2 ? 1 : naxes[2], type); + uint8_t *data = static_cast(img.data()); for (int i=1; i==1 || i<=naxes[2]; i++) { - cv::Mat tmp(h, w, cvtype); fpixel[2] = i; - fits_read_pix(file, fitstype, fpixel, size, NULL, tmp.ptr(), NULL, &status); - if(cvtype == CV_16S) - tmp.convertTo(tmp, CV_16U, 1, 32767); - cvimg.push_back(tmp); + fits_read_pix(file, fitstype, fpixel, size, NULL, data + img.size() * RawImage::typeSize(type) * (i-1), NULL, &status); + } + if(fitstype == TSHORT) + { + uint16_t *s = static_cast(img.data()); + size_t size = img.size() * img.channels(); + for(size_t i=0; iconvertToGLFormat(); } } noload: @@ -310,51 +319,33 @@ bool loadXISF(const QString &path, ImageInfoData &info, RawImage **image) info.info.append({QObject::tr("Height"), QString::number(xisfImage.height())}); if(!info.wcs->valid())info.wcs.reset(); + RawImage::DataType type; + switch(xisfImage.sampleFormat()) + { + case LibXISF::Image::UInt8: type = RawImage::UINT8; break; + case LibXISF::Image::UInt16: type = RawImage::UINT16; break; + case LibXISF::Image::UInt32: type = RawImage::UINT32; break; + case LibXISF::Image::Float32: type = RawImage::FLOAT32; break; + case LibXISF::Image::Float64: type = RawImage::FLOAT64; break; + default: break; + } + if(xisfImage.channelCount() == 1) { - switch(xisfImage.sampleFormat()) - { - case LibXISF::Image::UInt8: - *image = new RawImage(xisfImage.width(), xisfImage.height(), RawImage::UINT8); - std::memcpy((*image)->data(), xisfImage.imageData(), xisfImage.imageDataSize()); - break; - case LibXISF::Image::UInt16: - *image = new RawImage(xisfImage.width(), xisfImage.height(), RawImage::UINT16); - std::memcpy((*image)->data(), xisfImage.imageData(), xisfImage.imageDataSize()); - break; - case LibXISF::Image::Float32: - *image = new RawImage(xisfImage.width(), xisfImage.height(), RawImage::FLOAT32); - std::memcpy((*image)->data(), xisfImage.imageData(), xisfImage.imageDataSize()); - break; - default: - break; - } + *image = new RawImage(xisfImage.width(), xisfImage.height(), 1, type); + std::memcpy((*image)->data(), xisfImage.imageData(), xisfImage.imageDataSize()); } - else if(xisfImage.channelCount() == 3) + else if(xisfImage.channelCount() == 3 || xisfImage.channelCount() == 4) { LibXISF::Image tmpImage = xisfImage; - tmpImage.convertPixelStorageTo(LibXISF::Image::Normal); - - switch(tmpImage.sampleFormat()) - { - case LibXISF::Image::UInt8: - *image = new RawImage(tmpImage.width(), tmpImage.height(), RawImage::UINT8C3); - std::memcpy((*image)->data(), tmpImage.imageData(), tmpImage.imageDataSize()); - break; - case LibXISF::Image::UInt16: - *image = new RawImage(tmpImage.width(), tmpImage.height(), RawImage::UINT16C3); - std::memcpy((*image)->data(), tmpImage.imageData(), tmpImage.imageDataSize()); - break; - case LibXISF::Image::Float32: - *image = new RawImage(tmpImage.width(), tmpImage.height(), RawImage::FLOAT32C3); - std::memcpy((*image)->data(), tmpImage.imageData(), tmpImage.imageDataSize()); - break; - default: - break; - } + tmpImage.convertPixelStorageTo(LibXISF::Image::Planar); + *image = RawImage::fromPlanar(tmpImage.imageData(), tmpImage.width(), tmpImage.height(), tmpImage.channelCount(), type); } if(*image) + { + (*image)->convertToGLFormat(); return true; + } } catch (LibXISF::Error &err) { @@ -439,11 +430,7 @@ void LoadRunable::run() if(m_analyzeLevel >= Peaks) { std::vector peaks; - if(raw) { - rawImage->quarter(); - qDebug() << "quarter" << timer.restart(); - } - RawImage *medianImage = rawImage->medianFilter(); + /*RawImage *medianImage = rawImage->medianFilter(); qDebug() << "median" << timer.restart(); int numPeaks = medianImage->findPeaks(median+stdDev*2, 20, peaks); delete medianImage; @@ -483,7 +470,7 @@ void LoadRunable::run() info.info.append({QObject::tr("FWHM X"), QString::number(fwhmX/stars.size())}); info.info.append({QObject::tr("FWHM Y"), QString::number(fwhmY/stars.size())}); } - qDebug() << "Star fit" << timer.restart(); + qDebug() << "Star fit" << timer.restart();*/ } } @@ -498,10 +485,6 @@ void LoadRunable::run() else QMetaObject::invokeMethod(m_receiver, "imageLoaded", Qt::QueuedConnection, Q_ARG(void*, rawImage), Q_ARG(ImageInfoData, info)); } - catch(cv::Exception e) - { - qDebug() << e.what(); - } catch(std::exception e) { qDebug() << e.what(); @@ -566,40 +549,40 @@ void writeFITSImage(fitsfile *fw, RawImage *rawimage, ImageInfoData &imageinfo) int status = 0; long firstpix[3] = {1,1,1}; - int channels = rawimage->mat().channels(); + int channels = rawimage->channels(); int naxis = channels == 1 ? 2 : 3; - long naxes[3] = {(int)rawimage->width(), (int)rawimage->height(), rawimage->mat().channels()}; + long naxes[3] = {(int)rawimage->width(), (int)rawimage->height(), rawimage->channels()}; - std::vector mat; + std::vector planes; if(channels == 1) - mat.push_back(rawimage->mat()); + planes.push_back(*rawimage); else - cv::split(rawimage->mat(), mat); + planes = rawimage->split(); - switch(CV_MAT_DEPTH(rawimage->dataType())) + switch(rawimage->type()) { - case CV_8U: + case RawImage::UINT8: fits_create_img(fw, BYTE_IMG, naxis, naxes, &status); for(int i=0; isize(), mat[i].data, &status); + fits_write_pix(fw, TBYTE, firstpix, rawimage->size(), planes[i].data(), &status); } break; - case CV_16U: + case RawImage::UINT16: fits_create_img(fw, USHORT_IMG, naxis, naxes, &status); for(int i=0; isize(), mat[i].data, &status); + fits_write_pix(fw, TUSHORT, firstpix, rawimage->size(), planes[i].data(), &status); } break; - case CV_32F: + case RawImage::FLOAT32: fits_create_img(fw, FLOAT_IMG, naxis, naxes, &status); for(int i=0; isize(), mat[i].data, &status); + fits_write_pix(fw, TFLOAT, firstpix, rawimage->size(), planes[i].data(), &status); } break; } @@ -645,13 +628,13 @@ void ConvertRunable::run() try { LibXISF::XISFWriter xisf; - int channelCount = rawimage->mat().channels(); + int channelCount = rawimage->channels(); LibXISF::Image::SampleFormat sampleFormat; - switch(CV_MAT_DEPTH(rawimage->dataType())) + switch(rawimage->type()) { - case CV_8U: sampleFormat = LibXISF::Image::UInt8; break; - case CV_16U: sampleFormat = LibXISF::Image::UInt16; break; - case CV_32F: sampleFormat = LibXISF::Image::Float32; break; + case RawImage::UINT8: sampleFormat = LibXISF::Image::UInt8; break; + case RawImage::UINT16: sampleFormat = LibXISF::Image::UInt16; break; + case RawImage::FLOAT32: sampleFormat = LibXISF::Image::Float32; break; default: return; } diff --git a/rawimage.cpp b/rawimage.cpp index f43117e..0b66fe6 100644 --- a/rawimage.cpp +++ b/rawimage.cpp @@ -1,142 +1,129 @@ #include "rawimage.h" +#include +#include int THUMB_SIZE = 128; int THUMB_SIZE_BORDER = 138; int THUMB_SIZE_BORDER_Y = 158; double SATURATION = 0.95; -RawImage::ImgType CV2Type(int cvtype) +size_t RawImage::typeSize(RawImage::DataType type) { - switch (cvtype) + switch(type) { - case CV_8U: - return RawImage::UINT8; - case CV_16U: - return RawImage::UINT16; - case CV_32F: - return RawImage::FLOAT32; - case CV_8UC3: - return RawImage::UINT8C3; - case CV_8UC4: - return RawImage::UINT8C4; - case CV_16UC3: - return RawImage::UINT16C3; - case CV_16UC4: - return RawImage::UINT16C4; - case CV_32FC3: - return RawImage::FLOAT32C3; - default: - return RawImage::UNKNOWN; + 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; } } -int Type2CV(RawImage::ImgType type) +void RawImage::allocate(uint32_t w, uint32_t h, uint32_t ch, DataType type) { - switch (type) - { - case RawImage::UINT8: - return CV_8U; - case RawImage::UINT16: - return CV_16U; - case RawImage::FLOAT32: - return CV_32F; - case RawImage::UINT8C3: - return CV_8UC3; - case RawImage::UINT8C4: - return CV_8UC4; - case RawImage::UINT16C3: - return CV_16UC3; - case RawImage::UINT16C4: - return CV_16UC4; - case RawImage::FLOAT32C3: - return CV_32FC3; - case RawImage::UNKNOWN: - return CV_8S; - default: - return CV_8U; - } + m_width = w; + m_height = h; + m_channels = ch; + m_ch = ch == 3 ? 4 : ch; + m_origType = m_type = type; + m_pixels.reset(new PixelType[m_width * m_height * m_ch * typeSize(type)]); } RawImage::RawImage() { - m_stats = false; } -RawImage::RawImage(int w, int h, ImgType type) +RawImage::RawImage(uint32_t w, uint32_t h, uint32_t ch, DataType type) { - m_img.create(h, w, Type2CV(type)); - m_stats = false; -} - -RawImage::RawImage(cv::Mat &img) -{ - m_img = img; - m_stats = false; - scaleToUnit(); + allocate(w, h, ch, type); } RawImage::RawImage(const RawImage &d) { - d.m_img.copyTo(m_img); - m_mean = d.m_mean; - m_stdDev = d.m_stdDev; - m_median = d.m_median; - m_min = d.m_min; - m_max = d.m_max; - m_mad = d.m_mad; + 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) +{ + 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; + m_saturated = d.m_saturated; +} + RawImage::RawImage(const QImage &img) { - if(img.format() == QImage::Format_RGB32) + qDebug() << img; + if(img.format() == QImage::Format_RGBX8888) { - m_img.create(img.height(), img.width(), CV_8UC4); + allocate(img.width(), img.height(), 3, UINT8); for(int i=0; i &r) const { - r.resize(w*h); + /*r.resize(w*h); x -= w/2; y -= h/2; if(x<0)x = 0; @@ -215,12 +202,12 @@ void RawImage::rect(int &x, int &y, int w, int h, std::vector &r) const cv::Mat roiImg(m_img, cv::Rect(x, y, w, h)); cv::Mat doubleMat; roiImg.convertTo(doubleMat, CV_64F); - r = std::vector(doubleMat.begin(), doubleMat.end()); + r = std::vector(doubleMat.begin(), doubleMat.end());*/ } int RawImage::findPeaks(double background, double distance, std::vector &peaks) const { - std::vector> contours; + /*std::vector> contours; cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(distance, distance)); @@ -240,29 +227,22 @@ int RawImage::findPeaks(double background, double distance, std::vector &p peaks.push_back(Peak(1, contour[0].x, contour[0].y)); } - return peaks.size(); -} - -RawImage* RawImage::medianFilter() const -{ - RawImage *ret = new RawImage(); - cv::medianBlur(m_img, ret->m_img, 3); - return ret; -} - -void RawImage::quarter() -{ - + return peaks.size();*/ } uint32_t RawImage::width() const { - return m_img.cols; + return m_width; } uint32_t RawImage::height() const { - return m_img.rows; + return m_height; +} + +uint32_t RawImage::channels() const +{ + return m_channels; } uint32_t RawImage::size() const @@ -270,27 +250,21 @@ uint32_t RawImage::size() const return width()*height(); } -RawImage::ImgType RawImage::type() const +RawImage::DataType RawImage::type() const { - return CV2Type(m_img.type()); -} - -int RawImage::dataType() const -{ - return m_img.type(); + return m_type; } uint32_t RawImage::norm() const { - switch(m_img.type()) + switch(m_type) { - case CV_8U: - case CV_8UC3: - case CV_8UC4: + case UINT8: return UINT8_MAX; - case CV_16U: - case CV_16UC3: + case UINT16: return UINT16_MAX; + case UINT32: + return UINT32_MAX; default: return 1; } @@ -298,37 +272,113 @@ uint32_t RawImage::norm() const void* RawImage::data() { - return m_img.ptr(); + return m_pixels.get(); } const void *RawImage::data() const { - return m_img.ptr(); + 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); +} + +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); } void RawImage::convertToThumbnail() { m_thumbAspect = (float)width() / height(); - switch(CV_MAT_DEPTH(m_img.type())) + uint16_t *out = reinterpret_cast(new uint8_t[THUMB_SIZE * THUMB_SIZE * 4 * sizeof(uint16_t)]); + + auto loop = [&](uint16_t *out, auto *in, auto scale) { - case CV_8U: - m_img.convertTo(m_img, CV_16U, 255); + for(int i=0; i(m_pixels.get()), 256); break; - case CV_32F: - m_img.convertTo(m_img, CV_16U, 65535); + case UINT16: + loop(out, reinterpret_cast(m_pixels.get()), 1); break; - case CV_16U: + case UINT32: + loop(out, reinterpret_cast(m_pixels.get()), UINT16_MAX/(float)UINT32_MAX); + break; + case FLOAT32: + loop(out, reinterpret_cast(m_pixels.get()), 65535.0); break; default: - break; + qDebug() << "Should not happend"; + delete [] out; + return; } - if(m_img.channels() == 1) - cv::cvtColor(m_img, m_img, cv::COLOR_GRAY2RGB); - if(m_img.channels() == 4) - cv::cvtColor(m_img, m_img, cv::COLOR_RGBA2RGB); - cv::Size dsize(THUMB_SIZE, THUMB_SIZE); - cv::resize(m_img, m_img, dsize, 0, 0, cv::INTER_NEAREST); + m_pixels.reset(reinterpret_cast(out)); + 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(m_pixels.get()); + uint32_t *src = reinterpret_cast(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(m_pixels.get()); + double *src = reinterpret_cast(m_original.get()); + + for(size_t i = 0; i < s; i++) + dst[i] = src[i]; + } } float RawImage::thumbAspect() const @@ -336,69 +386,94 @@ float RawImage::thumbAspect() const return m_thumbAspect; } -const cv::Mat& RawImage::mat() const -{ - return m_img; -} - -bool RawImage::pixel(int x, int y, QVector3D &rgb) const +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_img.type()) + switch(m_origType) { - case CV_8U: + case UINT8: { - uint8_t v = m_img.at(y, x); - rgb = QVector3D(v, v, v); + const uint8_t *v = static_cast(origData(y, x)); + if(m_channels == 1) + { + r = g = b = *v; + } + else + { + r = v[0]; + g = v[1]; + b = v[2]; + } break; } - case CV_16U: + case UINT16: { - uint16_t v = m_img.at(y, x); - rgb = QVector3D(v, v, v); + const uint16_t *v = static_cast(origData(y, x)); + if(m_channels == 1) + { + r = g = b = *v; + } + else + { + r = v[0]; + g = v[1]; + b = v[2]; + } break; } - case CV_32F: + case UINT32: { - float v = m_img.at(y, x); - rgb = QVector3D(v, v, v); + const uint32_t *v = static_cast(origData(y, x)); + if(m_channels == 1) + { + r = g = b = *v; + } + else + { + r = v[0]; + g = v[1]; + b = v[2]; + } break; } - case CV_8UC3: + case FLOAT32: { - cv::Vec3b v = m_img.at(y, x); - rgb = QVector3D(v[0], v[1], v[2]); + const float *v = static_cast(origData(y, x)); + if(m_channels == 1) + { + r = g = b = *v; + } + else + { + r = v[0]; + g = v[1]; + b = v[2]; + } break; } - case CV_8UC4: + case FLOAT64: { - cv::Vec4b v = m_img.at(y, x); - rgb = QVector3D(v[0], v[1], v[2]); + const double *v = static_cast(origData(y, x)); + if(m_channels == 1) + { + r = g = b = *v; + } + else + { + r = v[0]; + g = v[1]; + b = v[2]; + } break; } - case CV_16UC3: - { - cv::Vec3w v = m_img.at(y, x); - rgb = QVector3D(v[0], v[1], v[2]); - break; - } - case CV_32FC3: - { - cv::Vec3f v = m_img.at(y, x); - rgb = QVector3D(v[0], v[1], v[2]); - break; - } - default: - rgb = QVector3D(0, 0, 0); - break; } return true; } void RawImage::scaleToUnit() { - if(CV_MAT_DEPTH(m_img.type()) == CV_32F) + /*if(CV_MAT_DEPTH(m_img.type()) == CV_32F) { double min, max; cv::minMaxIdx(m_img, &min, &max); @@ -408,15 +483,70 @@ void RawImage::scaleToUnit() float zero = min * scale; m_img = m_img * scale - zero; } - } + }*/ } void RawImage::downscaleTo(uint32_t size) { - if(size < width() || size < height()) + /*if(size < width() || size < height()) { double s = (double)size / std::max(width(), height()); cv::Size dsize(std::floor(width() * s), std::floor(height() * s)); cv::resize(m_img, m_img, dsize, 0, 0, cv::INTER_AREA); - } + }*/ +} + +RawImage *RawImage::fromPlanar(const RawImage &img) +{ + return RawImage::fromPlanar(img.data(), img.width(), img.height(), img.channels(), img.type()); +} + +RawImage *RawImage::fromPlanar(const void *pixels, uint32_t w, uint32_t h, uint32_t ch, RawImage::DataType type) +{ + RawImage *image = new 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(pixels), static_cast(image->data()), UINT8_MAX); + break; + case UINT16: + convert(static_cast(pixels), static_cast(image->data()), UINT16_MAX); + break; + case UINT32: + convert(static_cast(pixels), static_cast(image->data()), UINT32_MAX); + break; + case FLOAT32: + convert(static_cast(pixels), static_cast(image->data()), 1); + break; + case FLOAT64: + convert(static_cast(pixels), static_cast(image->data()), 1); + break; + } + + return image; +} + +std::vector RawImage::split() const +{ + std::vector planes; + planes.resize(m_channels); + for(size_t i=0; i #include +#include #include #include #include -#include #include -#include extern int THUMB_SIZE; extern int THUMB_SIZE_BORDER; @@ -38,55 +37,71 @@ public: class RawImage { -protected: - cv::Mat m_img; - bool m_stats; - double m_mean; - double m_stdDev; - double m_median; - double m_min; - double m_max; - double m_mad; - float m_thumbAspect; - uint32_t m_saturated; + using PixelType = uint8_t; public: - enum ImgType + enum DataType { UINT8, UINT16, + UINT32, FLOAT32, - UINT8C3, - UINT8C4, - UINT16C3, - UINT16C4, - FLOAT32C3, - UNKNOWN, + FLOAT64, }; +protected: + struct Stats + { + bool m_stats = false; + double m_mean[4] = {0.0}; + double m_stdDev[4] = {0.0}; + double m_median[4] = {0.0}; + double m_min[4] = {0.0}; + double m_max[4] = {0.0}; + double m_mad[4] = {0.0}; + }; + std::unique_ptr m_pixels; + std::unique_ptr m_original; + uint32_t m_width = 0; + uint32_t m_height = 0; + uint32_t m_channels = 0; + uint32_t m_ch = 0; + DataType m_type = UINT8; + DataType m_origType = UINT8; + float m_thumbAspect = 0.0; + uint32_t m_saturated = 0.0; + Stats m_stats; + void allocate(uint32_t w, uint32_t h, uint32_t ch, DataType type); +public: RawImage(); - RawImage(int w, int h, ImgType type); - RawImage(cv::Mat &img); + RawImage(uint32_t w, uint32_t h, uint32_t ch, DataType type); RawImage(const RawImage &d); + RawImage(RawImage &&d); RawImage(const QImage &img); bool imageStats(double *mean, double *stdDev, double *median, double *min, double *max, double *mad, uint32_t *saturated); void calcStats(); void rect(int &x, int &y, int w, int h, std::vector &r) const; int findPeaks(double background, double distance, std::vector &peaks) const; - RawImage* medianFilter() const; - void quarter(); uint32_t width() const; uint32_t height() const; + uint32_t channels() const; uint32_t size() const; - ImgType type() const; - int dataType() const; + DataType type() const; uint32_t norm() const; void* data(); const void* data() const; + void* data(uint32_t row, uint32_t col = 0); + const void* data(uint32_t row, uint32_t col = 0) const; + void *origData(uint32_t row, uint32_t col = 0) const; void convertToThumbnail(); + void convertToGLFormat(); float thumbAspect() const; - const cv::Mat& mat() const; - bool pixel(int x, int y, QVector3D &rgb) const; + bool pixel(int x, int y, double &r, double &g, double &b) const; void scaleToUnit(); void downscaleTo(uint32_t size); + + static RawImage* fromPlanar(const RawImage &img); + static RawImage* fromPlanar(const void *pixels, uint32_t w, uint32_t h, uint32_t ch, DataType type); + static size_t typeSize(DataType type); + std::vector split() const; }; #endif // RAWIMAGE_H