Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -86,6 +86,12 @@ find_package(Exiv2) set_package_properties(Exiv2 PROPERTIES URL "http://www.exiv2.org" DESCRIPTION "image metadata support" TYPE REQUIRED) +find_package(CFitsio) +if (CFITSIO_FOUND) + set(HAVE_FITS 1) +endif() +set_package_properties(CFitsio PROPERTIES DESCRIPTION "FITS IO Library" URL "http://heasarc.gsfc.nasa.gov/fitsio/fitsio.html" TYPE OPTIONAL PURPOSE "Support for the FITS (Flexible Image Transport System) data format.") + find_package(KF5Kipi) if (KF5Kipi_FOUND) set(KIPI_FOUND true) Index: cmake/FindCFitsio.cmake =================================================================== --- /dev/null +++ cmake/FindCFitsio.cmake @@ -0,0 +1,72 @@ +# - Try to find CFITSIO +# Once done this will define +# +# CFITSIO_FOUND - system has CFITSIO +# CFITSIO_INCLUDE_DIR - the CFITSIO include directory +# CFITSIO_LIBRARIES - Link these to use CFITSIO + +# Copyright (c) 2006, Jasem Mutlaq +# Based on FindLibfacile by Carsten Niehaus, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +if (CFITSIO_INCLUDE_DIR AND CFITSIO_LIBRARIES) + + # in cache already + set(CFITSIO_FOUND TRUE) + message(STATUS "Found CFITSIO: ${CFITSIO_LIBRARIES}") + + +else (CFITSIO_INCLUDE_DIR AND CFITSIO_LIBRARIES) + + if (NOT WIN32) + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_check_modules(PC_CFITSIO cfitsio) + endif (PKG_CONFIG_FOUND) + endif (NOT WIN32) + + if(ANDROID) + find_path(CFITSIO_INCLUDE_DIR fitsio.h + ${BUILD_KSTARSLITE_DIR}/include + NO_DEFAULT_PATH + ) + else(ANDROID) + find_path(CFITSIO_INCLUDE_DIR fitsio.h + ${PC_CFITSIO_INCLUDE_DIRS} + ${_obIncDir} + ${GNUWIN32_DIR}/include + ) + endif(ANDROID) + + find_library(CFITSIO_LIBRARIES NAMES cfitsio libcfitsio + PATHS + if(ANDROID) + ${BUILD_KSTARSLITE_DIR}/android_libs/${ANDROID_ARCHITECTURE} + else(ANDROID) + ${PC_CFITSIO_LIBRARY_DIRS} + ${_obIncDir} + ${GNUWIN32_DIR}/include + endif(ANDROID) + ) + + if(CFITSIO_INCLUDE_DIR AND CFITSIO_LIBRARIES) + set(CFITSIO_FOUND TRUE) + else (CFITSIO_INCLUDE_DIR AND CFITSIO_LIBRARIES) + set(CFITSIO_FOUND FALSE) + endif(CFITSIO_INCLUDE_DIR AND CFITSIO_LIBRARIES) + + if (CFITSIO_FOUND) + if (NOT CFitsio_FIND_QUIETLY) + message(STATUS "Found CFITSIO: ${CFITSIO_LIBRARIES}") + endif (NOT CFitsio_FIND_QUIETLY) + else (CFITSIO_FOUND) + if (CFitsio_FIND_REQUIRED) + message(FATAL_ERROR "CFITSIO not found. Please install libcfitsio-dev and try again.") + endif (CFitsio_FIND_REQUIRED) + endif (CFITSIO_FOUND) + + mark_as_advanced(CFITSIO_INCLUDE_DIR CFITSIO_LIBRARIES) + +endif (CFITSIO_INCLUDE_DIR AND CFITSIO_LIBRARIES) Index: config-gwenview.h.cmake =================================================================== --- config-gwenview.h.cmake +++ config-gwenview.h.cmake @@ -4,3 +4,4 @@ #cmakedefine KIPI_FOUND 1 #define GV_TEST_DATA_DIR "@CMAKE_CURRENT_SOURCE_DIR@/tests/data" #cmakedefine HAVE_X11 ${HAVE_X11} +#cmakedefine HAVE_FITS ${HAVE_FITS} Index: lib/CMakeLists.txt =================================================================== --- lib/CMakeLists.txt +++ lib/CMakeLists.txt @@ -169,6 +169,14 @@ ${GV_JPEG_DIR}/transupp.c ) +if (CFITSIO_FOUND) + set(gwenviewlib_SRCS + ${gwenviewlib_SRCS} + fitsformat/bayer.c + fitsformat/fitsdata.cpp + ) +endif() + if (NOT GWENVIEW_SEMANTICINFO_BACKEND_NONE) set(gwenviewlib_SRCS ${gwenviewlib_SRCS} @@ -239,6 +247,10 @@ if (WIN32) target_link_libraries(gwenviewlib ${EXPAT_LIBRARIES}) endif() +if (CFITSIO_FOUND) + target_link_libraries(gwenviewlib ${CFITSIO_LIBRARIES}) +endif() + if (KF5KDcraw_FOUND) target_link_libraries(gwenviewlib KF5::KDcraw) Index: lib/document/loadingdocumentimpl.cpp =================================================================== --- lib/document/loadingdocumentimpl.cpp +++ lib/document/loadingdocumentimpl.cpp @@ -63,6 +63,10 @@ #include "urlutils.h" #include "videodocumentloadedimpl.h" #include "gwenviewconfig.h" +#include "config-gwenview.h" +#if HAVE_FITS +#include "fitsformat/fitsdata.h" +#endif namespace Gwenview { @@ -239,6 +243,15 @@ // Set buffer again, otherwise QImageReader won't restart from scratch reader.setDevice(&buffer); if (!reader.canRead()) { +#if HAVE_FITS + FITSData fitsLoader; + + if (fitsLoader.loadFITS(q->document()->url().path())) { + mFormat = "image/fits"; + mImageSize = QSize((int)fitsLoader.getWidth(), (int)fitsLoader.getHeight()); + return true; + } +#endif qWarning() << "QImageReader::read() without format hint failed:" << reader.errorString(); return false; } @@ -291,6 +304,12 @@ void loadImageData() { +#if HAVE_FITS + if (mFormat == "image/fits") { + mImage = FITSData::FITSToImage(q->document()->url().path()); + return; + } +#endif QBuffer buffer; buffer.setBuffer(&mData); buffer.open(QIODevice::ReadOnly); Index: lib/fitsformat/bayer.h =================================================================== --- /dev/null +++ lib/fitsformat/bayer.h @@ -0,0 +1,275 @@ +/* + * 1394-Based Digital Camera Control Library + * + * Bayer pattern decoding functions + * + * Written by Damien Douxchamps and Frederic Devernay + * The original VNG and AHD Bayer decoding are from Dave Coffin's DCRAW. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#pragma once + +#include + +/** + * Error codes returned by most libdc1394 functions. + * + * General rule: 0 is success, negative denotes a problem. + */ +typedef enum { + DC1394_SUCCESS = 0, + DC1394_FAILURE = -1, + DC1394_NOT_A_CAMERA = -2, + DC1394_FUNCTION_NOT_SUPPORTED = -3, + DC1394_CAMERA_NOT_INITIALIZED = -4, + DC1394_MEMORY_ALLOCATION_FAILURE = -5, + DC1394_TAGGED_REGISTER_NOT_FOUND = -6, + DC1394_NO_ISO_CHANNEL = -7, + DC1394_NO_BANDWIDTH = -8, + DC1394_IOCTL_FAILURE = -9, + DC1394_CAPTURE_IS_NOT_SET = -10, + DC1394_CAPTURE_IS_RUNNING = -11, + DC1394_RAW1394_FAILURE = -12, + DC1394_FORMAT7_ERROR_FLAG_1 = -13, + DC1394_FORMAT7_ERROR_FLAG_2 = -14, + DC1394_INVALID_ARGUMENT_VALUE = -15, + DC1394_REQ_VALUE_OUTSIDE_RANGE = -16, + DC1394_INVALID_FEATURE = -17, + DC1394_INVALID_VIDEO_FORMAT = -18, + DC1394_INVALID_VIDEO_MODE = -19, + DC1394_INVALID_FRAMERATE = -20, + DC1394_INVALID_TRIGGER_MODE = -21, + DC1394_INVALID_TRIGGER_SOURCE = -22, + DC1394_INVALID_ISO_SPEED = -23, + DC1394_INVALID_IIDC_VERSION = -24, + DC1394_INVALID_COLOR_CODING = -25, + DC1394_INVALID_COLOR_FILTER = -26, + DC1394_INVALID_CAPTURE_POLICY = -27, + DC1394_INVALID_ERROR_CODE = -28, + DC1394_INVALID_BAYER_METHOD = -29, + DC1394_INVALID_VIDEO1394_DEVICE = -30, + DC1394_INVALID_OPERATION_MODE = -31, + DC1394_INVALID_TRIGGER_POLARITY = -32, + DC1394_INVALID_FEATURE_MODE = -33, + DC1394_INVALID_LOG_TYPE = -34, + DC1394_INVALID_BYTE_ORDER = -35, + DC1394_INVALID_STEREO_METHOD = -36, + DC1394_BASLER_NO_MORE_SFF_CHUNKS = -37, + DC1394_BASLER_CORRUPTED_SFF_CHUNK = -38, + DC1394_BASLER_UNKNOWN_SFF_CHUNK = -39 +} dc1394error_t; +#define DC1394_ERROR_MIN DC1394_BASLER_UNKNOWN_SFF_CHUNK +#define DC1394_ERROR_MAX DC1394_SUCCESS +#define DC1394_ERROR_NUM (DC1394_ERROR_MAX - DC1394_ERROR_MIN + 1) + +/** + * Enumeration of video modes. Note that the notion of IIDC "format" is not present here, except in the format_7 name. + */ +typedef enum { + DC1394_VIDEO_MODE_160x120_YUV444 = 64, + DC1394_VIDEO_MODE_320x240_YUV422, + DC1394_VIDEO_MODE_640x480_YUV411, + DC1394_VIDEO_MODE_640x480_YUV422, + DC1394_VIDEO_MODE_640x480_RGB8, + DC1394_VIDEO_MODE_640x480_MONO8, + DC1394_VIDEO_MODE_640x480_MONO16, + DC1394_VIDEO_MODE_800x600_YUV422, + DC1394_VIDEO_MODE_800x600_RGB8, + DC1394_VIDEO_MODE_800x600_MONO8, + DC1394_VIDEO_MODE_1024x768_YUV422, + DC1394_VIDEO_MODE_1024x768_RGB8, + DC1394_VIDEO_MODE_1024x768_MONO8, + DC1394_VIDEO_MODE_800x600_MONO16, + DC1394_VIDEO_MODE_1024x768_MONO16, + DC1394_VIDEO_MODE_1280x960_YUV422, + DC1394_VIDEO_MODE_1280x960_RGB8, + DC1394_VIDEO_MODE_1280x960_MONO8, + DC1394_VIDEO_MODE_1600x1200_YUV422, + DC1394_VIDEO_MODE_1600x1200_RGB8, + DC1394_VIDEO_MODE_1600x1200_MONO8, + DC1394_VIDEO_MODE_1280x960_MONO16, + DC1394_VIDEO_MODE_1600x1200_MONO16, + DC1394_VIDEO_MODE_EXIF, + DC1394_VIDEO_MODE_FORMAT7_0, + DC1394_VIDEO_MODE_FORMAT7_1, + DC1394_VIDEO_MODE_FORMAT7_2, + DC1394_VIDEO_MODE_FORMAT7_3, + DC1394_VIDEO_MODE_FORMAT7_4, + DC1394_VIDEO_MODE_FORMAT7_5, + DC1394_VIDEO_MODE_FORMAT7_6, + DC1394_VIDEO_MODE_FORMAT7_7 +} dc1394video_mode_t; +#define DC1394_VIDEO_MODE_MIN DC1394_VIDEO_MODE_160x120_YUV444 +#define DC1394_VIDEO_MODE_MAX DC1394_VIDEO_MODE_FORMAT7_7 +#define DC1394_VIDEO_MODE_NUM (DC1394_VIDEO_MODE_MAX - DC1394_VIDEO_MODE_MIN + 1) + +/* Special min/max are defined for Format_7 */ +#define DC1394_VIDEO_MODE_FORMAT7_MIN DC1394_VIDEO_MODE_FORMAT7_0 +#define DC1394_VIDEO_MODE_FORMAT7_MAX DC1394_VIDEO_MODE_FORMAT7_7 +#define DC1394_VIDEO_MODE_FORMAT7_NUM (DC1394_VIDEO_MODE_FORMAT7_MAX - DC1394_VIDEO_MODE_FORMAT7_MIN + 1) + +/** + * Enumeration of colour codings. For details on the data format please read the IIDC specifications. + */ +typedef enum { + DC1394_COLOR_CODING_MONO8 = 352, + DC1394_COLOR_CODING_YUV411, + DC1394_COLOR_CODING_YUV422, + DC1394_COLOR_CODING_YUV444, + DC1394_COLOR_CODING_RGB8, + DC1394_COLOR_CODING_MONO16, + DC1394_COLOR_CODING_RGB16, + DC1394_COLOR_CODING_MONO16S, + DC1394_COLOR_CODING_RGB16S, + DC1394_COLOR_CODING_RAW8, + DC1394_COLOR_CODING_RAW16 +} dc1394color_coding_t; +#define DC1394_COLOR_CODING_MIN DC1394_COLOR_CODING_MONO8 +#define DC1394_COLOR_CODING_MAX DC1394_COLOR_CODING_RAW16 +#define DC1394_COLOR_CODING_NUM (DC1394_COLOR_CODING_MAX - DC1394_COLOR_CODING_MIN + 1) + +/** + * RAW sensor filters. These elementary tiles tesselate the image plane in RAW modes. RGGB should be interpreted in 2D as + * + * RG + * GB + * + * and similarly for other filters. + */ +typedef enum { + DC1394_COLOR_FILTER_RGGB = 512, + DC1394_COLOR_FILTER_GBRG, + DC1394_COLOR_FILTER_GRBG, + DC1394_COLOR_FILTER_BGGR +} dc1394color_filter_t; +#define DC1394_COLOR_FILTER_MIN DC1394_COLOR_FILTER_RGGB +#define DC1394_COLOR_FILTER_MAX DC1394_COLOR_FILTER_BGGR +#define DC1394_COLOR_FILTER_NUM (DC1394_COLOR_FILTER_MAX - DC1394_COLOR_FILTER_MIN + 1) + +/** + * Byte order for YUV formats (may be expanded to RGB in the future) + * + * IIDC cameras always return data in UYVY order, but conversion functions can change this if requested. + */ +typedef enum { DC1394_BYTE_ORDER_UYVY = 800, DC1394_BYTE_ORDER_YUYV } dc1394byte_order_t; +#define DC1394_BYTE_ORDER_MIN DC1394_BYTE_ORDER_UYVY +#define DC1394_BYTE_ORDER_MAX DC1394_BYTE_ORDER_YUYV +#define DC1394_BYTE_ORDER_NUM (DC1394_BYTE_ORDER_MAX - DC1394_BYTE_ORDER_MIN + 1) + +/** + * A struct containing a list of color codings + */ +typedef struct +{ + uint32_t num; + dc1394color_coding_t codings[DC1394_COLOR_CODING_NUM]; +} dc1394color_codings_t; + +/** + * A struct containing a list of video modes + */ +typedef struct +{ + uint32_t num; + dc1394video_mode_t modes[DC1394_VIDEO_MODE_NUM]; +} dc1394video_modes_t; + +/** + * Yet another boolean data type + */ +typedef enum { DC1394_FALSE = 0, DC1394_TRUE } dc1394bool_t; + +/** + * Yet another boolean data type, a bit more oriented towards electrical-engineers + */ +typedef enum { DC1394_OFF = 0, DC1394_ON } dc1394switch_t; + +/** + * A list of de-mosaicing techniques for Bayer-patterns. + * + * The speed of the techniques can vary greatly, as well as their quality. + */ +typedef enum { + DC1394_BAYER_METHOD_NEAREST = 0, + DC1394_BAYER_METHOD_SIMPLE, + DC1394_BAYER_METHOD_BILINEAR, + DC1394_BAYER_METHOD_HQLINEAR, + DC1394_BAYER_METHOD_DOWNSAMPLE, + DC1394_BAYER_METHOD_EDGESENSE, + DC1394_BAYER_METHOD_VNG, + DC1394_BAYER_METHOD_AHD +} dc1394bayer_method_t; +#define DC1394_BAYER_METHOD_MIN DC1394_BAYER_METHOD_NEAREST +#define DC1394_BAYER_METHOD_MAX DC1394_BAYER_METHOD_AHD +#define DC1394_BAYER_METHOD_NUM (DC1394_BAYER_METHOD_MAX - DC1394_BAYER_METHOD_MIN + 1) + +typedef struct +{ + dc1394bayer_method_t method; /* Debayer method */ + dc1394color_filter_t filter; /* Debayer pattern */ + int offsetX, offsetY; /* Debayer offset */ +} BayerParams; + +/************************************************************************************************ + * * + * Color conversion functions for cameras that can output raw Bayer pattern images (color * + * codings DC1394_COLOR_CODING_RAW8 and DC1394_COLOR_CODING_RAW16). * + * * + * Credits and sources: * + * - Nearest Neighbor : OpenCV library * + * - Bilinear : OpenCV library * + * - HQLinear : High-Quality Linear Interpolation For Demosaicing Of Bayer-Patterned * + * Color Images, by Henrique S. Malvar, Li-wei He, and Ross Cutler, * + * in Proceedings of the ICASSP'04 Conference. * + * - Edge Sense II : Laroche, Claude A. "Apparatus and method for adaptively interpolating * + * a full color image utilizing chrominance gradients" * + * U.S. Patent 5,373,322. Based on the code found on the website * + * http://www-ise.stanford.edu/~tingchen/ Converted to C and adapted to * + * all four elementary patterns. * + * - Downsample : "Known to the Ancients" * + * - Simple : Implemented from the information found in the manual of Allied Vision * + * Technologies (AVT) cameras. * + * - VNG : Variable Number of Gradients, a method described in * + * http://www-ise.stanford.edu/~tingchen/algodep/vargra.html * + * Sources import from DCRAW by Frederic Devernay. DCRAW is a RAW * + * converter program by Dave Coffin. URL: * + * http://www.cybercom.net/~dcoffin/dcraw/ * + * - AHD : Adaptive Homogeneity-Directed Demosaicing Algorithm, by K. Hirakawa * + * and T.W. Parks, IEEE Transactions on Image Processing, Vol. 14, Nr. 3, * + * March 2005, pp. 360 - 369. * + * * + ************************************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Perform de-mosaicing on an 8-bit image buffer + */ +dc1394error_t dc1394_bayer_decoding_8bit(const uint8_t *bayer, uint8_t *rgb, uint32_t width, uint32_t height, + dc1394color_filter_t tile, dc1394bayer_method_t method); + +/** + * Perform de-mosaicing on an 16-bit image buffer + */ +dc1394error_t dc1394_bayer_decoding_16bit(const uint16_t *bayer, uint16_t *rgb, uint32_t width, uint32_t height, + dc1394color_filter_t tile, dc1394bayer_method_t method, uint32_t bits); + +#ifdef __cplusplus +} +#endif Index: lib/fitsformat/bayer.c =================================================================== --- /dev/null +++ lib/fitsformat/bayer.c @@ -0,0 +1,2485 @@ +/* + * 1394-Based Digital Camera Control Library + * + * Bayer pattern decoding functions + * + * Written by Damien Douxchamps and Frederic Devernay + * The original VNG and AHD Bayer decoding are from Dave Coffin's DCRAW. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "bayer.h" + +#include +#include +#include +#include +#include + +#define CLIP(in, out) \ + in = in < 0 ? 0 : in; \ + in = in > 255 ? 255 : in; \ + out = in; + +#define CLIP16(in, out, bits) \ + in = in < 0 ? 0 : in; \ + in = in > ((1 << bits) - 1) ? ((1 << bits) - 1) : in; \ + out = in; + +void ClearBorders(uint8_t *rgb, int sx, int sy, int w) +{ + int i, j; + /* black edges are added with a width w: */ + i = 3 * sx * w - 1; + j = 3 * sx * sy - 1; + while (i >= 0) + { + rgb[i--] = 0; + rgb[j--] = 0; + } + + int low = sx * (w - 1) * 3 - 1 + w * 3; + i = low + sx * (sy - w * 2 + 1) * 3; + while (i > low) + { + j = 6 * w; + while (j > 0) + { + rgb[i--] = 0; + j--; + } + i -= (sx - 2 * w) * 3; + } +} + +void ClearBorders_uint16(uint16_t *rgb, int sx, int sy, int w) +{ + int i, j; + + /* black edges: */ + i = 3 * sx * w - 1; + j = 3 * sx * sy - 1; + while (i >= 0) + { + rgb[i--] = 0; + rgb[j--] = 0; + } + + int low = sx * (w - 1) * 3 - 1 + w * 3; + i = low + sx * (sy - w * 2 + 1) * 3; + while (i > low) + { + j = 6 * w; + while (j > 0) + { + rgb[i--] = 0; + j--; + } + i -= (sx - 2 * w) * 3; + } +} + +/************************************************************** + * Color conversion functions for cameras that can * + * output raw-Bayer pattern images, such as some Basler and * + * Point Grey camera. Most of the algos presented here come * + * from http://www-ise.stanford.edu/~tingchen/ and have been * + * converted from Matlab to C and extended to all elementary * + * patterns. * + **************************************************************/ + +/* 8-bits versions */ +/* insprired by OpenCV's Bayer decoding */ + +dc1394error_t dc1394_bayer_NearestNeighbor(const uint8_t *bayer, uint8_t *rgb, int sx, int sy, + int tile) +{ + const int bayerStep = sx; + const int rgbStep = 3 * sx; + int width = sx; + int height = sy; + int blue = tile == DC1394_COLOR_FILTER_BGGR || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; + int start_with_green = tile == DC1394_COLOR_FILTER_GBRG || tile == DC1394_COLOR_FILTER_GRBG; + int i, imax, iinc; + + if ((tile > DC1394_COLOR_FILTER_MAX) || (tile < DC1394_COLOR_FILTER_MIN)) + return DC1394_INVALID_COLOR_FILTER; + + /* add black border */ + imax = sx * sy * 3; + for (i = sx * (sy - 1) * 3; i < imax; i++) + { + rgb[i] = 0; + } + iinc = (sx - 1) * 3; + for (i = (sx - 1) * 3; i < imax; i += iinc) + { + rgb[i++] = 0; + rgb[i++] = 0; + rgb[i++] = 0; + } + + rgb += 1; + width -= 1; + height -= 1; + + for (; height--; bayer += bayerStep, rgb += rgbStep) + { + const uint8_t *bayerEnd = bayer + width; + + if (start_with_green) + { + rgb[-blue] = bayer[1]; + rgb[0] = bayer[bayerStep + 1]; + rgb[blue] = bayer[bayerStep]; + bayer++; + rgb += 3; + } + + if (blue > 0) + { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) + { + rgb[-1] = bayer[0]; + rgb[0] = bayer[1]; + rgb[1] = bayer[bayerStep + 1]; + + rgb[2] = bayer[2]; + rgb[3] = bayer[bayerStep + 2]; + rgb[4] = bayer[bayerStep + 1]; + } + } + else + { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) + { + rgb[1] = bayer[0]; + rgb[0] = bayer[1]; + rgb[-1] = bayer[bayerStep + 1]; + + rgb[4] = bayer[2]; + rgb[3] = bayer[bayerStep + 2]; + rgb[2] = bayer[bayerStep + 1]; + } + } + + if (bayer < bayerEnd) + { + rgb[-blue] = bayer[0]; + rgb[0] = bayer[1]; + rgb[blue] = bayer[bayerStep + 1]; + bayer++; + rgb += 3; + } + + bayer -= width; + rgb -= width * 3; + + blue = -blue; + start_with_green = !start_with_green; + } + + return DC1394_SUCCESS; +} + +/* OpenCV's Bayer decoding */ +dc1394error_t dc1394_bayer_Bilinear(const uint8_t *bayer, uint8_t *rgb, int sx, int sy, int tile) +{ + const int bayerStep = sx; + const int rgbStep = 3 * sx; + int width = sx; + int height = sy; + /* + the two letters of the OpenCV name are respectively + the 4th and 3rd letters from the blinky name, + and we also have to switch R and B (OpenCV is BGR) + + CV_BayerBG2BGR <-> DC1394_COLOR_FILTER_BGGR + CV_BayerGB2BGR <-> DC1394_COLOR_FILTER_GBRG + CV_BayerGR2BGR <-> DC1394_COLOR_FILTER_GRBG + + int blue = tile == CV_BayerBG2BGR || tile == CV_BayerGB2BGR ? -1 : 1; + int start_with_green = tile == CV_BayerGB2BGR || tile == CV_BayerGR2BGR; + */ + int blue = tile == DC1394_COLOR_FILTER_BGGR || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; + int start_with_green = tile == DC1394_COLOR_FILTER_GBRG || tile == DC1394_COLOR_FILTER_GRBG; + + if ((tile > DC1394_COLOR_FILTER_MAX) || (tile < DC1394_COLOR_FILTER_MIN)) + return DC1394_INVALID_COLOR_FILTER; + + ClearBorders(rgb, sx, sy, 1); + rgb += rgbStep + 3 + 1; + height -= 2; + width -= 2; + + for (; height--; bayer += bayerStep, rgb += rgbStep) + { + int t0, t1; + const uint8_t *bayerEnd = bayer + width; + + if (start_with_green) + { + /* OpenCV has a bug in the next line, which was + t0 = (bayer[0] + bayer[bayerStep * 2] + 1) >> 1; */ + t0 = (bayer[1] + bayer[bayerStep * 2 + 1] + 1) >> 1; + t1 = (bayer[bayerStep] + bayer[bayerStep + 2] + 1) >> 1; + rgb[-blue] = (uint8_t)t0; + rgb[0] = bayer[bayerStep + 1]; + rgb[blue] = (uint8_t)t1; + bayer++; + rgb += 3; + } + + if (blue > 0) + { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) + { + t0 = (bayer[0] + bayer[2] + bayer[bayerStep * 2] + bayer[bayerStep * 2 + 2] + 2) >> 2; + t1 = (bayer[1] + bayer[bayerStep] + bayer[bayerStep + 2] + bayer[bayerStep * 2 + 1] + 2) >> 2; + rgb[-1] = (uint8_t)t0; + rgb[0] = (uint8_t)t1; + rgb[1] = bayer[bayerStep + 1]; + + t0 = (bayer[2] + bayer[bayerStep * 2 + 2] + 1) >> 1; + t1 = (bayer[bayerStep + 1] + bayer[bayerStep + 3] + 1) >> 1; + rgb[2] = (uint8_t)t0; + rgb[3] = bayer[bayerStep + 2]; + rgb[4] = (uint8_t)t1; + } + } + else + { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) + { + t0 = (bayer[0] + bayer[2] + bayer[bayerStep * 2] + bayer[bayerStep * 2 + 2] + 2) >> 2; + t1 = (bayer[1] + bayer[bayerStep] + bayer[bayerStep + 2] + bayer[bayerStep * 2 + 1] + 2) >> 2; + rgb[1] = (uint8_t)t0; + rgb[0] = (uint8_t)t1; + rgb[-1] = bayer[bayerStep + 1]; + + t0 = (bayer[2] + bayer[bayerStep * 2 + 2] + 1) >> 1; + t1 = (bayer[bayerStep + 1] + bayer[bayerStep + 3] + 1) >> 1; + rgb[4] = (uint8_t)t0; + rgb[3] = bayer[bayerStep + 2]; + rgb[2] = (uint8_t)t1; + } + } + + if (bayer < bayerEnd) + { + t0 = (bayer[0] + bayer[2] + bayer[bayerStep * 2] + bayer[bayerStep * 2 + 2] + 2) >> 2; + t1 = (bayer[1] + bayer[bayerStep] + bayer[bayerStep + 2] + bayer[bayerStep * 2 + 1] + 2) >> 2; + rgb[-blue] = (uint8_t)t0; + rgb[0] = (uint8_t)t1; + rgb[blue] = bayer[bayerStep + 1]; + bayer++; + rgb += 3; + } + + bayer -= width; + rgb -= width * 3; + + blue = -blue; + start_with_green = !start_with_green; + } + return DC1394_SUCCESS; +} + +/* High-Quality Linear Interpolation For Demosaicing Of + Bayer-Patterned Color Images, by Henrique S. Malvar, Li-wei He, and + Ross Cutler, in ICASSP'04 */ +dc1394error_t dc1394_bayer_HQLinear(const uint8_t *bayer, uint8_t *rgb, int sx, int sy, int tile) +{ + const int bayerStep = sx; + const int rgbStep = 3 * sx; + int width = sx; + int height = sy; + int blue = tile == DC1394_COLOR_FILTER_BGGR || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; + int start_with_green = tile == DC1394_COLOR_FILTER_GBRG || tile == DC1394_COLOR_FILTER_GRBG; + + if ((tile > DC1394_COLOR_FILTER_MAX) || (tile < DC1394_COLOR_FILTER_MIN)) + return DC1394_INVALID_COLOR_FILTER; + + ClearBorders(rgb, sx, sy, 2); + rgb += 2 * rgbStep + 6 + 1; + height -= 4; + width -= 4; + + /* We begin with a (+1 line,+1 column) offset with respect to bilinear decoding, so start_with_green is the same, but blue is opposite */ + blue = -blue; + + for (; height--; bayer += bayerStep, rgb += rgbStep) + { + int t0, t1; + const uint8_t *bayerEnd = bayer + width; + const int bayerStep2 = bayerStep * 2; + const int bayerStep3 = bayerStep * 3; + const int bayerStep4 = bayerStep * 4; + + if (start_with_green) + { + /* at green pixel */ + rgb[0] = bayer[bayerStep2 + 2]; + t0 = rgb[0] * 5 + ((bayer[bayerStep + 2] + bayer[bayerStep3 + 2]) << 2) - bayer[2] - bayer[bayerStep + 1] - + bayer[bayerStep + 3] - bayer[bayerStep3 + 1] - bayer[bayerStep3 + 3] - bayer[bayerStep4 + 2] + + ((bayer[bayerStep2] + bayer[bayerStep2 + 4] + 1) >> 1); + t1 = rgb[0] * 5 + ((bayer[bayerStep2 + 1] + bayer[bayerStep2 + 3]) << 2) - bayer[bayerStep2] - + bayer[bayerStep + 1] - bayer[bayerStep + 3] - bayer[bayerStep3 + 1] - bayer[bayerStep3 + 3] - + bayer[bayerStep2 + 4] + ((bayer[2] + bayer[bayerStep4 + 2] + 1) >> 1); + t0 = (t0 + 4) >> 3; + CLIP(t0, rgb[-blue]); + t1 = (t1 + 4) >> 3; + CLIP(t1, rgb[blue]); + bayer++; + rgb += 3; + } + + if (blue > 0) + { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) + { + /* B at B */ + rgb[1] = bayer[bayerStep2 + 2]; + /* R at B */ + t0 = ((bayer[bayerStep + 1] + bayer[bayerStep + 3] + bayer[bayerStep3 + 1] + bayer[bayerStep3 + 3]) + << 1) - + (((bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) * 3 + 1) >> 1) + + rgb[1] * 6; + /* G at B */ + t1 = ((bayer[bayerStep + 2] + bayer[bayerStep2 + 1] + bayer[bayerStep2 + 3] + bayer[bayerStep3 + 2]) + << 1) - + (bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) + (rgb[1] << 2); + t0 = (t0 + 4) >> 3; + CLIP(t0, rgb[-1]); + t1 = (t1 + 4) >> 3; + CLIP(t1, rgb[0]); + /* at green pixel */ + rgb[3] = bayer[bayerStep2 + 3]; + t0 = rgb[3] * 5 + ((bayer[bayerStep + 3] + bayer[bayerStep3 + 3]) << 2) - bayer[3] - + bayer[bayerStep + 2] - bayer[bayerStep + 4] - bayer[bayerStep3 + 2] - bayer[bayerStep3 + 4] - + bayer[bayerStep4 + 3] + ((bayer[bayerStep2 + 1] + bayer[bayerStep2 + 5] + 1) >> 1); + t1 = rgb[3] * 5 + ((bayer[bayerStep2 + 2] + bayer[bayerStep2 + 4]) << 2) - bayer[bayerStep2 + 1] - + bayer[bayerStep + 2] - bayer[bayerStep + 4] - bayer[bayerStep3 + 2] - bayer[bayerStep3 + 4] - + bayer[bayerStep2 + 5] + ((bayer[3] + bayer[bayerStep4 + 3] + 1) >> 1); + t0 = (t0 + 4) >> 3; + CLIP(t0, rgb[2]); + t1 = (t1 + 4) >> 3; + CLIP(t1, rgb[4]); + } + } + else + { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) + { + /* R at R */ + rgb[-1] = bayer[bayerStep2 + 2]; + /* B at R */ + t0 = ((bayer[bayerStep + 1] + bayer[bayerStep + 3] + bayer[bayerStep3 + 1] + bayer[bayerStep3 + 3]) + << 1) - + (((bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) * 3 + 1) >> 1) + + rgb[-1] * 6; + /* G at R */ + t1 = ((bayer[bayerStep + 2] + bayer[bayerStep2 + 1] + bayer[bayerStep2 + 3] + bayer[bayerStep * 3 + 2]) + << 1) - + (bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) + (rgb[-1] << 2); + t0 = (t0 + 4) >> 3; + CLIP(t0, rgb[1]); + t1 = (t1 + 4) >> 3; + CLIP(t1, rgb[0]); + + /* at green pixel */ + rgb[3] = bayer[bayerStep2 + 3]; + t0 = rgb[3] * 5 + ((bayer[bayerStep + 3] + bayer[bayerStep3 + 3]) << 2) - bayer[3] - + bayer[bayerStep + 2] - bayer[bayerStep + 4] - bayer[bayerStep3 + 2] - bayer[bayerStep3 + 4] - + bayer[bayerStep4 + 3] + ((bayer[bayerStep2 + 1] + bayer[bayerStep2 + 5] + 1) >> 1); + t1 = rgb[3] * 5 + ((bayer[bayerStep2 + 2] + bayer[bayerStep2 + 4]) << 2) - bayer[bayerStep2 + 1] - + bayer[bayerStep + 2] - bayer[bayerStep + 4] - bayer[bayerStep3 + 2] - bayer[bayerStep3 + 4] - + bayer[bayerStep2 + 5] + ((bayer[3] + bayer[bayerStep4 + 3] + 1) >> 1); + t0 = (t0 + 4) >> 3; + CLIP(t0, rgb[4]); + t1 = (t1 + 4) >> 3; + CLIP(t1, rgb[2]); + } + } + + if (bayer < bayerEnd) + { + /* B at B */ + rgb[blue] = bayer[bayerStep2 + 2]; + /* R at B */ + t0 = ((bayer[bayerStep + 1] + bayer[bayerStep + 3] + bayer[bayerStep3 + 1] + bayer[bayerStep3 + 3]) << 1) - + (((bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) * 3 + 1) >> 1) + + rgb[blue] * 6; + /* G at B */ + t1 = (((bayer[bayerStep + 2] + bayer[bayerStep2 + 1] + bayer[bayerStep2 + 3] + bayer[bayerStep3 + 2])) + << 1) - + (bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) + (rgb[blue] << 2); + t0 = (t0 + 4) >> 3; + CLIP(t0, rgb[-blue]); + t1 = (t1 + 4) >> 3; + CLIP(t1, rgb[0]); + bayer++; + rgb += 3; + } + + bayer -= width; + rgb -= width * 3; + + blue = -blue; + start_with_green = !start_with_green; + } + + return DC1394_SUCCESS; +} + +/* coriander's Bayer decoding */ +/* Edge Sensing Interpolation II from http://www-ise.stanford.edu/~tingchen/ */ +/* (Laroche,Claude A. "Apparatus and method for adaptively + interpolating a full color image utilizing chrominance gradients" + U.S. Patent 5,373,322) */ +dc1394error_t dc1394_bayer_EdgeSense(const uint8_t *bayer, uint8_t *rgb, int sx, int sy, int tile) +{ + uint8_t *outR, *outG, *outB; + register int i3, j3, base; + int i, j; + int dh, dv; + int tmp; + int sx3 = sx * 3; + + /* sx and sy should be even */ + switch (tile) + { + case DC1394_COLOR_FILTER_GRBG: + case DC1394_COLOR_FILTER_BGGR: + outR = &rgb[0]; + outG = &rgb[1]; + outB = &rgb[2]; + break; + case DC1394_COLOR_FILTER_GBRG: + case DC1394_COLOR_FILTER_RGGB: + outR = &rgb[2]; + outG = &rgb[1]; + outB = &rgb[0]; + break; + default: + return DC1394_INVALID_COLOR_FILTER; + } + + switch (tile) + { + case DC1394_COLOR_FILTER_GRBG: + case DC1394_COLOR_FILTER_GBRG: + /* copy original RGB data to output images */ + for (i = 0, i3 = 0; i < sy * sx; i += (sx << 1), i3 += (sx3 << 1)) + { + for (j = 0, j3 = 0; j < sx; j += 2, j3 += 6) + { + base = i3 + j3; + outG[base] = bayer[i + j]; + outG[base + sx3 + 3] = bayer[i + j + sx + 1]; + outR[base + 3] = bayer[i + j + 1]; + outB[base + sx3] = bayer[i + j + sx]; + } + } + /* process GREEN channel */ + for (i3 = 3 * sx3; i3 < (sy - 2) * sx3; i3 += (sx3 << 1)) + { + for (j3 = 6; j3 < sx3 - 9; j3 += 6) + { + base = i3 + j3; + dh = abs(((outB[base - 6] + outB[base + 6]) >> 1) - outB[base]); + dv = abs(((outB[base - (sx3 << 1)] + outB[base + (sx3 << 1)]) >> 1) - outB[base]); + tmp = (((outG[base - 3] + outG[base + 3]) >> 1) * (dh <= dv) + + ((outG[base - sx3] + outG[base + sx3]) >> 1) * (dh > dv)); + CLIP(tmp, outG[base]); + } + } + + for (i3 = 2 * sx3; i3 < (sy - 3) * sx3; i3 += (sx3 << 1)) + { + for (j3 = 9; j3 < sx3 - 6; j3 += 6) + { + base = i3 + j3; + dh = abs(((outR[base - 6] + outR[base + 6]) >> 1) - outR[base]); + dv = abs(((outR[base - (sx3 << 1)] + outR[base + (sx3 << 1)]) >> 1) - outR[base]); + tmp = (((outG[base - 3] + outG[base + 3]) >> 1) * (dh <= dv) + + ((outG[base - sx3] + outG[base + sx3]) >> 1) * (dh > dv)); + CLIP(tmp, outG[base]); + } + } + /* process RED channel */ + for (i3 = 0; i3 < (sy - 1) * sx3; i3 += (sx3 << 1)) + { + for (j3 = 6; j3 < sx3 - 3; j3 += 6) + { + base = i3 + j3; + tmp = outG[base] + ((outR[base - 3] - outG[base - 3] + outR[base + 3] - outG[base + 3]) >> 1); + CLIP(tmp, outR[base]); + } + } + for (i3 = sx3; i3 < (sy - 2) * sx3; i3 += (sx3 << 1)) + { + for (j3 = 3; j3 < sx3; j3 += 6) + { + base = i3 + j3; + tmp = + outG[base] + ((outR[base - sx3] - outG[base - sx3] + outR[base + sx3] - outG[base + sx3]) >> 1); + CLIP(tmp, outR[base]); + } + for (j3 = 6; j3 < sx3 - 3; j3 += 6) + { + base = i3 + j3; + tmp = outG[base] + ((outR[base - sx3 - 3] - outG[base - sx3 - 3] + outR[base - sx3 + 3] - + outG[base - sx3 + 3] + outR[base + sx3 - 3] - outG[base + sx3 - 3] + + outR[base + sx3 + 3] - outG[base + sx3 + 3]) >> + 2); + CLIP(tmp, outR[base]); + } + } + + /* process BLUE channel */ + for (i3 = sx3; i3 < sy * sx3; i3 += (sx3 << 1)) + { + for (j3 = 3; j3 < sx3 - 6; j3 += 6) + { + base = i3 + j3; + tmp = outG[base] + ((outB[base - 3] - outG[base - 3] + outB[base + 3] - outG[base + 3]) >> 1); + CLIP(tmp, outB[base]); + } + } + for (i3 = 2 * sx3; i3 < (sy - 1) * sx3; i3 += (sx3 << 1)) + { + for (j3 = 0; j3 < sx3 - 3; j3 += 6) + { + base = i3 + j3; + tmp = + outG[base] + ((outB[base - sx3] - outG[base - sx3] + outB[base + sx3] - outG[base + sx3]) >> 1); + CLIP(tmp, outB[base]); + } + for (j3 = 3; j3 < sx3 - 6; j3 += 6) + { + base = i3 + j3; + tmp = outG[base] + ((outB[base - sx3 - 3] - outG[base - sx3 - 3] + outB[base - sx3 + 3] - + outG[base - sx3 + 3] + outB[base + sx3 - 3] - outG[base + sx3 - 3] + + outB[base + sx3 + 3] - outG[base + sx3 + 3]) >> + 2); + CLIP(tmp, outB[base]); + } + } + break; + + case DC1394_COLOR_FILTER_BGGR: + case DC1394_COLOR_FILTER_RGGB: + /* copy original RGB data to output images */ + for (i = 0, i3 = 0; i < sy * sx; i += (sx << 1), i3 += (sx3 << 1)) + { + for (j = 0, j3 = 0; j < sx; j += 2, j3 += 6) + { + base = i3 + j3; + outB[base] = bayer[i + j]; + outR[base + sx3 + 3] = bayer[i + sx + (j + 1)]; + outG[base + 3] = bayer[i + j + 1]; + outG[base + sx3] = bayer[i + sx + j]; + } + } + /* process GREEN channel */ + for (i3 = 2 * sx3; i3 < (sy - 2) * sx3; i3 += (sx3 << 1)) + { + for (j3 = 6; j3 < sx3 - 9; j3 += 6) + { + base = i3 + j3; + dh = abs(((outB[base - 6] + outB[base + 6]) >> 1) - outB[base]); + dv = abs(((outB[base - (sx3 << 1)] + outB[base + (sx3 << 1)]) >> 1) - outB[base]); + tmp = (((outG[base - 3] + outG[base + 3]) >> 1) * (dh <= dv) + + ((outG[base - sx3] + outG[base + sx3]) >> 1) * (dh > dv)); + CLIP(tmp, outG[base]); + } + } + for (i3 = 3 * sx3; i3 < (sy - 3) * sx3; i3 += (sx3 << 1)) + { + for (j3 = 9; j3 < sx3 - 6; j3 += 6) + { + base = i3 + j3; + dh = abs(((outR[base - 6] + outR[base + 6]) >> 1) - outR[base]); + dv = abs(((outR[base - (sx3 << 1)] + outR[base + (sx3 << 1)]) >> 1) - outR[base]); + tmp = (((outG[base - 3] + outG[base + 3]) >> 1) * (dh <= dv) + + ((outG[base - sx3] + outG[base + sx3]) >> 1) * (dh > dv)); + CLIP(tmp, outG[base]); + } + } + /* process RED channel */ + for (i3 = sx3; i3 < (sy - 1) * sx3; i3 += (sx3 << 1)) + { /* G-points (1/2) */ + for (j3 = 6; j3 < sx3 - 3; j3 += 6) + { + base = i3 + j3; + tmp = outG[base] + ((outR[base - 3] - outG[base - 3] + outR[base + 3] - outG[base + 3]) >> 1); + CLIP(tmp, outR[base]); + } + } + for (i3 = 2 * sx3; i3 < (sy - 2) * sx3; i3 += (sx3 << 1)) + { + for (j3 = 3; j3 < sx3; j3 += 6) + { /* G-points (2/2) */ + base = i3 + j3; + tmp = + outG[base] + ((outR[base - sx3] - outG[base - sx3] + outR[base + sx3] - outG[base + sx3]) >> 1); + CLIP(tmp, outR[base]); + } + for (j3 = 6; j3 < sx3 - 3; j3 += 6) + { /* B-points */ + base = i3 + j3; + tmp = outG[base] + ((outR[base - sx3 - 3] - outG[base - sx3 - 3] + outR[base - sx3 + 3] - + outG[base - sx3 + 3] + outR[base + sx3 - 3] - outG[base + sx3 - 3] + + outR[base + sx3 + 3] - outG[base + sx3 + 3]) >> + 2); + CLIP(tmp, outR[base]); + } + } + + /* process BLUE channel */ + for (i = 0, i3 = 0; i < sy * sx; i += (sx << 1), i3 += (sx3 << 1)) + { + for (j = 1, j3 = 3; j < sx - 2; j += 2, j3 += 6) + { + base = i3 + j3; + tmp = outG[base] + ((outB[base - 3] - outG[base - 3] + outB[base + 3] - outG[base + 3]) >> 1); + CLIP(tmp, outB[base]); + } + } + for (i3 = sx3; i3 < (sy - 1) * sx3; i3 += (sx3 << 1)) + { + for (j3 = 0; j3 < sx3 - 3; j3 += 6) + { + base = i3 + j3; + tmp = + outG[base] + ((outB[base - sx3] - outG[base - sx3] + outB[base + sx3] - outG[base + sx3]) >> 1); + CLIP(tmp, outB[base]); + } + for (j3 = 3; j3 < sx3 - 6; j3 += 6) + { + base = i3 + j3; + tmp = outG[base] + ((outB[base - sx3 - 3] - outG[base - sx3 - 3] + outB[base - sx3 + 3] - + outG[base - sx3 + 3] + outB[base + sx3 - 3] - outG[base + sx3 - 3] + + outB[base + sx3 + 3] - outG[base + sx3 + 3]) >> + 2); + CLIP(tmp, outB[base]); + } + } + break; + } + + ClearBorders(rgb, sx, sy, 3); + + return DC1394_SUCCESS; +} + +/* coriander's Bayer decoding */ +dc1394error_t dc1394_bayer_Downsample(const uint8_t *bayer, uint8_t *rgb, int sx, int sy, int tile) +{ + uint8_t *outR, *outG, *outB; + register int i, j; + int tmp; + + switch (tile) + { + case DC1394_COLOR_FILTER_GRBG: + case DC1394_COLOR_FILTER_BGGR: + outR = &rgb[0]; + outG = &rgb[1]; + outB = &rgb[2]; + break; + case DC1394_COLOR_FILTER_GBRG: + case DC1394_COLOR_FILTER_RGGB: + outR = &rgb[2]; + outG = &rgb[1]; + outB = &rgb[0]; + break; + default: + return DC1394_INVALID_COLOR_FILTER; + } + + switch (tile) + { + case DC1394_COLOR_FILTER_GRBG: + case DC1394_COLOR_FILTER_GBRG: + for (i = 0; i < sy * sx; i += (sx << 1)) + { + for (j = 0; j < sx; j += 2) + { + tmp = ((bayer[i + j] + bayer[i + sx + j + 1]) >> 1); + CLIP(tmp, outG[((i >> 2) + (j >> 1)) * 3]); + tmp = bayer[i + j + 1]; + CLIP(tmp, outR[((i >> 2) + (j >> 1)) * 3]); + tmp = bayer[i + sx + j]; + CLIP(tmp, outB[((i >> 2) + (j >> 1)) * 3]); + } + } + break; + case DC1394_COLOR_FILTER_BGGR: + case DC1394_COLOR_FILTER_RGGB: + for (i = 0; i < sy * sx; i += (sx << 1)) + { + for (j = 0; j < sx; j += 2) + { + tmp = ((bayer[i + sx + j] + bayer[i + j + 1]) >> 1); + CLIP(tmp, outG[((i >> 2) + (j >> 1)) * 3]); + tmp = bayer[i + sx + j + 1]; + CLIP(tmp, outR[((i >> 2) + (j >> 1)) * 3]); + tmp = bayer[i + j]; + CLIP(tmp, outB[((i >> 2) + (j >> 1)) * 3]); + } + } + break; + } + + return DC1394_SUCCESS; +} + +/* this is the method used inside AVT cameras. See AVT docs. */ +dc1394error_t dc1394_bayer_Simple(const uint8_t *bayer, uint8_t *rgb, int sx, int sy, int tile) +{ + const int bayerStep = sx; + const int rgbStep = 3 * sx; + int width = sx; + int height = sy; + int blue = tile == DC1394_COLOR_FILTER_BGGR || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; + int start_with_green = tile == DC1394_COLOR_FILTER_GBRG || tile == DC1394_COLOR_FILTER_GRBG; + int i, imax, iinc; + + if ((tile > DC1394_COLOR_FILTER_MAX) || (tile < DC1394_COLOR_FILTER_MIN)) + return DC1394_INVALID_COLOR_FILTER; + + /* add black border */ + imax = sx * sy * 3; + for (i = sx * (sy - 1) * 3; i < imax; i++) + { + rgb[i] = 0; + } + iinc = (sx - 1) * 3; + for (i = (sx - 1) * 3; i < imax; i += iinc) + { + rgb[i++] = 0; + rgb[i++] = 0; + rgb[i++] = 0; + } + + rgb += 1; + width -= 1; + height -= 1; + + for (; height--; bayer += bayerStep, rgb += rgbStep) + { + const uint8_t *bayerEnd = bayer + width; + + if (start_with_green) + { + rgb[-blue] = bayer[1]; + rgb[0] = (bayer[0] + bayer[bayerStep + 1] + 1) >> 1; + rgb[blue] = bayer[bayerStep]; + bayer++; + rgb += 3; + } + + if (blue > 0) + { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) + { + rgb[-1] = bayer[0]; + rgb[0] = (bayer[1] + bayer[bayerStep] + 1) >> 1; + rgb[1] = bayer[bayerStep + 1]; + + rgb[2] = bayer[2]; + rgb[3] = (bayer[1] + bayer[bayerStep + 2] + 1) >> 1; + rgb[4] = bayer[bayerStep + 1]; + } + } + else + { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) + { + rgb[1] = bayer[0]; + rgb[0] = (bayer[1] + bayer[bayerStep] + 1) >> 1; + rgb[-1] = bayer[bayerStep + 1]; + + rgb[4] = bayer[2]; + rgb[3] = (bayer[1] + bayer[bayerStep + 2] + 1) >> 1; + rgb[2] = bayer[bayerStep + 1]; + } + } + + if (bayer < bayerEnd) + { + rgb[-blue] = bayer[0]; + rgb[0] = (bayer[1] + bayer[bayerStep] + 1) >> 1; + rgb[blue] = bayer[bayerStep + 1]; + bayer++; + rgb += 3; + } + + bayer -= width; + rgb -= width * 3; + + blue = -blue; + start_with_green = !start_with_green; + } + + return DC1394_SUCCESS; +} + +/* 16-bits versions */ + +/* insprired by OpenCV's Bayer decoding */ +dc1394error_t dc1394_bayer_NearestNeighbor_uint16(const uint16_t *bayer, uint16_t *rgb, int sx, + int sy, int tile, int bits) +{ + (void)bits; + const int bayerStep = sx; + const int rgbStep = 3 * sx; + int width = sx; + int height = sy; + int blue = tile == DC1394_COLOR_FILTER_BGGR || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; + int start_with_green = tile == DC1394_COLOR_FILTER_GBRG || tile == DC1394_COLOR_FILTER_GRBG; + int i, iinc, imax; + + if ((tile > DC1394_COLOR_FILTER_MAX) || (tile < DC1394_COLOR_FILTER_MIN)) + return DC1394_INVALID_COLOR_FILTER; + + /* add black border */ + imax = sx * sy * 3; + for (i = sx * (sy - 1) * 3; i < imax; i++) + { + rgb[i] = 0; + } + iinc = (sx - 1) * 3; + for (i = (sx - 1) * 3; i < imax; i += iinc) + { + rgb[i++] = 0; + rgb[i++] = 0; + rgb[i++] = 0; + } + + rgb += 1; + height -= 1; + width -= 1; + + for (; height--; bayer += bayerStep, rgb += rgbStep) + { + const uint16_t *bayerEnd = bayer + width; + + if (start_with_green) + { + rgb[-blue] = bayer[1]; + rgb[0] = bayer[bayerStep + 1]; + rgb[blue] = bayer[bayerStep]; + bayer++; + rgb += 3; + } + + if (blue > 0) + { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) + { + rgb[-1] = bayer[0]; + rgb[0] = bayer[1]; + rgb[1] = bayer[bayerStep + 1]; + + rgb[2] = bayer[2]; + rgb[3] = bayer[bayerStep + 2]; + rgb[4] = bayer[bayerStep + 1]; + } + } + else + { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) + { + rgb[1] = bayer[0]; + rgb[0] = bayer[1]; + rgb[-1] = bayer[bayerStep + 1]; + + rgb[4] = bayer[2]; + rgb[3] = bayer[bayerStep + 2]; + rgb[2] = bayer[bayerStep + 1]; + } + } + + if (bayer < bayerEnd) + { + rgb[-blue] = bayer[0]; + rgb[0] = bayer[1]; + rgb[blue] = bayer[bayerStep + 1]; + bayer++; + rgb += 3; + } + + bayer -= width; + rgb -= width * 3; + + blue = -blue; + start_with_green = !start_with_green; + } + + return DC1394_SUCCESS; +} +/* OpenCV's Bayer decoding */ +dc1394error_t dc1394_bayer_Bilinear_uint16(const uint16_t *bayer, uint16_t *rgb, int sx, int sy, + int tile, int bits) +{ + (void)bits; + const int bayerStep = sx; + const int rgbStep = 3 * sx; + int width = sx; + int height = sy; + int blue = tile == DC1394_COLOR_FILTER_BGGR || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; + int start_with_green = tile == DC1394_COLOR_FILTER_GBRG || tile == DC1394_COLOR_FILTER_GRBG; + + if ((tile > DC1394_COLOR_FILTER_MAX) || (tile < DC1394_COLOR_FILTER_MIN)) + return DC1394_INVALID_COLOR_FILTER; + + rgb += rgbStep + 3 + 1; + height -= 2; + width -= 2; + + for (; height--; bayer += bayerStep, rgb += rgbStep) + { + int t0, t1; + const uint16_t *bayerEnd = bayer + width; + + if (start_with_green) + { + /* OpenCV has a bug in the next line, which was + t0 = (bayer[0] + bayer[bayerStep * 2] + 1) >> 1; */ + t0 = (bayer[1] + bayer[bayerStep * 2 + 1] + 1) >> 1; + t1 = (bayer[bayerStep] + bayer[bayerStep + 2] + 1) >> 1; + rgb[-blue] = (uint16_t)t0; + rgb[0] = bayer[bayerStep + 1]; + rgb[blue] = (uint16_t)t1; + bayer++; + rgb += 3; + } + + if (blue > 0) + { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) + { + t0 = (bayer[0] + bayer[2] + bayer[bayerStep * 2] + bayer[bayerStep * 2 + 2] + 2) >> 2; + t1 = (bayer[1] + bayer[bayerStep] + bayer[bayerStep + 2] + bayer[bayerStep * 2 + 1] + 2) >> 2; + rgb[-1] = (uint16_t)t0; + rgb[0] = (uint16_t)t1; + rgb[1] = bayer[bayerStep + 1]; + + t0 = (bayer[2] + bayer[bayerStep * 2 + 2] + 1) >> 1; + t1 = (bayer[bayerStep + 1] + bayer[bayerStep + 3] + 1) >> 1; + rgb[2] = (uint16_t)t0; + rgb[3] = bayer[bayerStep + 2]; + rgb[4] = (uint16_t)t1; + } + } + else + { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) + { + t0 = (bayer[0] + bayer[2] + bayer[bayerStep * 2] + bayer[bayerStep * 2 + 2] + 2) >> 2; + t1 = (bayer[1] + bayer[bayerStep] + bayer[bayerStep + 2] + bayer[bayerStep * 2 + 1] + 2) >> 2; + rgb[1] = (uint16_t)t0; + rgb[0] = (uint16_t)t1; + rgb[-1] = bayer[bayerStep + 1]; + + t0 = (bayer[2] + bayer[bayerStep * 2 + 2] + 1) >> 1; + t1 = (bayer[bayerStep + 1] + bayer[bayerStep + 3] + 1) >> 1; + rgb[4] = (uint16_t)t0; + rgb[3] = bayer[bayerStep + 2]; + rgb[2] = (uint16_t)t1; + } + } + + if (bayer < bayerEnd) + { + t0 = (bayer[0] + bayer[2] + bayer[bayerStep * 2] + bayer[bayerStep * 2 + 2] + 2) >> 2; + t1 = (bayer[1] + bayer[bayerStep] + bayer[bayerStep + 2] + bayer[bayerStep * 2 + 1] + 2) >> 2; + rgb[-blue] = (uint16_t)t0; + rgb[0] = (uint16_t)t1; + rgb[blue] = bayer[bayerStep + 1]; + bayer++; + rgb += 3; + } + + bayer -= width; + rgb -= width * 3; + + blue = -blue; + start_with_green = !start_with_green; + } + + return DC1394_SUCCESS; +} + +/* High-Quality Linear Interpolation For Demosaicing Of + Bayer-Patterned Color Images, by Henrique S. Malvar, Li-wei He, and + Ross Cutler, in ICASSP'04 */ +dc1394error_t dc1394_bayer_HQLinear_uint16(const uint16_t *bayer, uint16_t *rgb, int sx, int sy, + int tile, int bits) +{ + const int bayerStep = sx; + const int rgbStep = 3 * sx; + int width = sx; + int height = sy; + /* + the two letters of the OpenCV name are respectively + the 4th and 3rd letters from the blinky name, + and we also have to switch R and B (OpenCV is BGR) + + CV_BayerBG2BGR <-> DC1394_COLOR_FILTER_BGGR + CV_BayerGB2BGR <-> DC1394_COLOR_FILTER_GBRG + CV_BayerGR2BGR <-> DC1394_COLOR_FILTER_GRBG + + int blue = tile == CV_BayerBG2BGR || tile == CV_BayerGB2BGR ? -1 : 1; + int start_with_green = tile == CV_BayerGB2BGR || tile == CV_BayerGR2BGR; + */ + int blue = tile == DC1394_COLOR_FILTER_BGGR || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; + int start_with_green = tile == DC1394_COLOR_FILTER_GBRG || tile == DC1394_COLOR_FILTER_GRBG; + + if ((tile > DC1394_COLOR_FILTER_MAX) || (tile < DC1394_COLOR_FILTER_MIN)) + return DC1394_INVALID_COLOR_FILTER; + + ClearBorders_uint16(rgb, sx, sy, 2); + rgb += 2 * rgbStep + 6 + 1; + height -= 4; + width -= 4; + + /* We begin with a (+1 line,+1 column) offset with respect to bilinear decoding, so start_with_green is the same, but blue is opposite */ + blue = -blue; + + for (; height--; bayer += bayerStep, rgb += rgbStep) + { + int t0, t1; + const uint16_t *bayerEnd = bayer + width; + const int bayerStep2 = bayerStep * 2; + const int bayerStep3 = bayerStep * 3; + const int bayerStep4 = bayerStep * 4; + + if (start_with_green) + { + /* at green pixel */ + rgb[0] = bayer[bayerStep2 + 2]; + t0 = rgb[0] * 5 + ((bayer[bayerStep + 2] + bayer[bayerStep3 + 2]) << 2) - bayer[2] - bayer[bayerStep + 1] - + bayer[bayerStep + 3] - bayer[bayerStep3 + 1] - bayer[bayerStep3 + 3] - bayer[bayerStep4 + 2] + + ((bayer[bayerStep2] + bayer[bayerStep2 + 4] + 1) >> 1); + t1 = rgb[0] * 5 + ((bayer[bayerStep2 + 1] + bayer[bayerStep2 + 3]) << 2) - bayer[bayerStep2] - + bayer[bayerStep + 1] - bayer[bayerStep + 3] - bayer[bayerStep3 + 1] - bayer[bayerStep3 + 3] - + bayer[bayerStep2 + 4] + ((bayer[2] + bayer[bayerStep4 + 2] + 1) >> 1); + t0 = (t0 + 4) >> 3; + CLIP16(t0, rgb[-blue], bits); + t1 = (t1 + 4) >> 3; + CLIP16(t1, rgb[blue], bits); + bayer++; + rgb += 3; + } + + if (blue > 0) + { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) + { + /* B at B */ + rgb[1] = bayer[bayerStep2 + 2]; + /* R at B */ + t0 = ((bayer[bayerStep + 1] + bayer[bayerStep + 3] + bayer[bayerStep3 + 1] + bayer[bayerStep3 + 3]) + << 1) - + (((bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) * 3 + 1) >> 1) + + rgb[1] * 6; + /* G at B */ + t1 = ((bayer[bayerStep + 2] + bayer[bayerStep2 + 1] + bayer[bayerStep2 + 3] + bayer[bayerStep * 3 + 2]) + << 1) - + (bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) + (rgb[1] << 2); + t0 = (t0 + 4) >> 3; + CLIP16(t0, rgb[-1], bits); + t1 = (t1 + 4) >> 3; + CLIP16(t1, rgb[0], bits); + /* at green pixel */ + rgb[3] = bayer[bayerStep2 + 3]; + t0 = rgb[3] * 5 + ((bayer[bayerStep + 3] + bayer[bayerStep3 + 3]) << 2) - bayer[3] - + bayer[bayerStep + 2] - bayer[bayerStep + 4] - bayer[bayerStep3 + 2] - bayer[bayerStep3 + 4] - + bayer[bayerStep4 + 3] + ((bayer[bayerStep2 + 1] + bayer[bayerStep2 + 5] + 1) >> 1); + t1 = rgb[3] * 5 + ((bayer[bayerStep2 + 2] + bayer[bayerStep2 + 4]) << 2) - bayer[bayerStep2 + 1] - + bayer[bayerStep + 2] - bayer[bayerStep + 4] - bayer[bayerStep3 + 2] - bayer[bayerStep3 + 4] - + bayer[bayerStep2 + 5] + ((bayer[3] + bayer[bayerStep4 + 3] + 1) >> 1); + t0 = (t0 + 4) >> 3; + CLIP16(t0, rgb[2], bits); + t1 = (t1 + 4) >> 3; + CLIP16(t1, rgb[4], bits); + } + } + else + { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) + { + /* R at R */ + rgb[-1] = bayer[bayerStep2 + 2]; + /* B at R */ + t0 = ((bayer[bayerStep + 1] + bayer[bayerStep + 3] + bayer[bayerStep * 3 + 1] + bayer[bayerStep3 + 3]) + << 1) - + (((bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) * 3 + 1) >> 1) + + rgb[-1] * 6; + /* G at R */ + t1 = ((bayer[bayerStep + 2] + bayer[bayerStep2 + 1] + bayer[bayerStep2 + 3] + bayer[bayerStep3 + 2]) + << 1) - + (bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) + (rgb[-1] << 2); + t0 = (t0 + 4) >> 3; + CLIP16(t0, rgb[1], bits); + t1 = (t1 + 4) >> 3; + CLIP16(t1, rgb[0], bits); + + /* at green pixel */ + rgb[3] = bayer[bayerStep2 + 3]; + t0 = rgb[3] * 5 + ((bayer[bayerStep + 3] + bayer[bayerStep3 + 3]) << 2) - bayer[3] - + bayer[bayerStep + 2] - bayer[bayerStep + 4] - bayer[bayerStep3 + 2] - bayer[bayerStep3 + 4] - + bayer[bayerStep4 + 3] + ((bayer[bayerStep2 + 1] + bayer[bayerStep2 + 5] + 1) >> 1); + t1 = rgb[3] * 5 + ((bayer[bayerStep2 + 2] + bayer[bayerStep2 + 4]) << 2) - bayer[bayerStep2 + 1] - + bayer[bayerStep + 2] - bayer[bayerStep + 4] - bayer[bayerStep3 + 2] - bayer[bayerStep3 + 4] - + bayer[bayerStep2 + 5] + ((bayer[3] + bayer[bayerStep4 + 3] + 1) >> 1); + t0 = (t0 + 4) >> 3; + CLIP16(t0, rgb[4], bits); + t1 = (t1 + 4) >> 3; + CLIP16(t1, rgb[2], bits); + } + } + + if (bayer < bayerEnd) + { + /* B at B */ + rgb[blue] = bayer[bayerStep2 + 2]; + /* R at B */ + t0 = ((bayer[bayerStep + 1] + bayer[bayerStep + 3] + bayer[bayerStep3 + 1] + bayer[bayerStep3 + 3]) << 1) - + (((bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) * 3 + 1) >> 1) + + rgb[blue] * 6; + /* G at B */ + t1 = (((bayer[bayerStep + 2] + bayer[bayerStep2 + 1] + bayer[bayerStep2 + 3] + bayer[bayerStep3 + 2])) + << 1) - + (bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) + (rgb[blue] << 2); + t0 = (t0 + 4) >> 3; + CLIP16(t0, rgb[-blue], bits); + t1 = (t1 + 4) >> 3; + CLIP16(t1, rgb[0], bits); + bayer++; + rgb += 3; + } + + bayer -= width; + rgb -= width * 3; + + blue = -blue; + start_with_green = !start_with_green; + } + + return DC1394_SUCCESS; +} + +/* coriander's Bayer decoding */ +dc1394error_t dc1394_bayer_EdgeSense_uint16(const uint16_t *bayer, uint16_t *rgb, int sx, int sy, + int tile, int bits) +{ + uint16_t *outR, *outG, *outB; + register int i3, j3, base; + int i, j; + int dh, dv; + int tmp; + int sx3 = sx * 3; + + /* sx and sy should be even */ + switch (tile) + { + case DC1394_COLOR_FILTER_GRBG: + case DC1394_COLOR_FILTER_BGGR: + outR = &rgb[0]; + outG = &rgb[1]; + outB = &rgb[2]; + break; + case DC1394_COLOR_FILTER_GBRG: + case DC1394_COLOR_FILTER_RGGB: + outR = &rgb[2]; + outG = &rgb[1]; + outB = &rgb[0]; + break; + default: + return DC1394_INVALID_COLOR_FILTER; + } + + switch (tile) + { + case DC1394_COLOR_FILTER_GRBG: + case DC1394_COLOR_FILTER_GBRG: + /* copy original RGB data to output images */ + for (i = 0, i3 = 0; i < sy * sx; i += (sx << 1), i3 += (sx3 << 1)) + { + for (j = 0, j3 = 0; j < sx; j += 2, j3 += 6) + { + base = i3 + j3; + outG[base] = bayer[i + j]; + outG[base + sx3 + 3] = bayer[i + j + sx + 1]; + outR[base + 3] = bayer[i + j + 1]; + outB[base + sx3] = bayer[i + j + sx]; + } + } + /* process GREEN channel */ + for (i3 = 3 * sx3; i3 < (sy - 2) * sx3; i3 += (sx3 << 1)) + { + for (j3 = 6; j3 < sx3 - 9; j3 += 6) + { + base = i3 + j3; + dh = abs(((outB[base - 6] + outB[base + 6]) >> 1) - outB[base]); + dv = abs(((outB[base - (sx3 << 1)] + outB[base + (sx3 << 1)]) >> 1) - outB[base]); + tmp = (((outG[base - 3] + outG[base + 3]) >> 1) * (dh <= dv) + + ((outG[base - sx3] + outG[base + sx3]) >> 1) * (dh > dv)); + CLIP16(tmp, outG[base], bits); + } + } + + for (i3 = 2 * sx3; i3 < (sy - 3) * sx3; i3 += (sx3 << 1)) + { + for (j3 = 9; j3 < sx3 - 6; j3 += 6) + { + base = i3 + j3; + dh = abs(((outR[base - 6] + outR[base + 6]) >> 1) - outR[base]); + dv = abs(((outR[base - (sx3 << 1)] + outR[base + (sx3 << 1)]) >> 1) - outR[base]); + tmp = (((outG[base - 3] + outG[base + 3]) >> 1) * (dh <= dv) + + ((outG[base - sx3] + outG[base + sx3]) >> 1) * (dh > dv)); + CLIP16(tmp, outG[base], bits); + } + } + /* process RED channel */ + for (i3 = 0; i3 < (sy - 1) * sx3; i3 += (sx3 << 1)) + { + for (j3 = 6; j3 < sx3 - 3; j3 += 6) + { + base = i3 + j3; + tmp = outG[base] + ((outR[base - 3] - outG[base - 3] + outR[base + 3] - outG[base + 3]) >> 1); + CLIP16(tmp, outR[base], bits); + } + } + for (i3 = sx3; i3 < (sy - 2) * sx3; i3 += (sx3 << 1)) + { + for (j3 = 3; j3 < sx3; j3 += 6) + { + base = i3 + j3; + tmp = + outG[base] + ((outR[base - sx3] - outG[base - sx3] + outR[base + sx3] - outG[base + sx3]) >> 1); + CLIP16(tmp, outR[base], bits); + } + for (j3 = 6; j3 < sx3 - 3; j3 += 6) + { + base = i3 + j3; + tmp = outG[base] + ((outR[base - sx3 - 3] - outG[base - sx3 - 3] + outR[base - sx3 + 3] - + outG[base - sx3 + 3] + outR[base + sx3 - 3] - outG[base + sx3 - 3] + + outR[base + sx3 + 3] - outG[base + sx3 + 3]) >> + 2); + CLIP16(tmp, outR[base], bits); + } + } + + /* process BLUE channel */ + for (i3 = sx3; i3 < sy * sx3; i3 += (sx3 << 1)) + { + for (j3 = 3; j3 < sx3 - 6; j3 += 6) + { + base = i3 + j3; + tmp = outG[base] + ((outB[base - 3] - outG[base - 3] + outB[base + 3] - outG[base + 3]) >> 1); + CLIP16(tmp, outB[base], bits); + } + } + for (i3 = 2 * sx3; i3 < (sy - 1) * sx3; i3 += (sx3 << 1)) + { + for (j3 = 0; j3 < sx3 - 3; j3 += 6) + { + base = i3 + j3; + tmp = + outG[base] + ((outB[base - sx3] - outG[base - sx3] + outB[base + sx3] - outG[base + sx3]) >> 1); + CLIP16(tmp, outB[base], bits); + } + for (j3 = 3; j3 < sx3 - 6; j3 += 6) + { + base = i3 + j3; + tmp = outG[base] + ((outB[base - sx3 - 3] - outG[base - sx3 - 3] + outB[base - sx3 + 3] - + outG[base - sx3 + 3] + outB[base + sx3 - 3] - outG[base + sx3 - 3] + + outB[base + sx3 + 3] - outG[base + sx3 + 3]) >> + 2); + CLIP16(tmp, outB[base], bits); + } + } + break; + + case DC1394_COLOR_FILTER_BGGR: + case DC1394_COLOR_FILTER_RGGB: + /* copy original RGB data to output images */ + for (i = 0, i3 = 0; i < sy * sx; i += (sx << 1), i3 += (sx3 << 1)) + { + for (j = 0, j3 = 0; j < sx; j += 2, j3 += 6) + { + base = i3 + j3; + outB[base] = bayer[i + j]; + outR[base + sx3 + 3] = bayer[i + sx + (j + 1)]; + outG[base + 3] = bayer[i + j + 1]; + outG[base + sx3] = bayer[i + sx + j]; + } + } + /* process GREEN channel */ + for (i3 = 2 * sx3; i3 < (sy - 2) * sx3; i3 += (sx3 << 1)) + { + for (j3 = 6; j3 < sx3 - 9; j3 += 6) + { + base = i3 + j3; + dh = abs(((outB[base - 6] + outB[base + 6]) >> 1) - outB[base]); + dv = abs(((outB[base - (sx3 << 1)] + outB[base + (sx3 << 1)]) >> 1) - outB[base]); + tmp = (((outG[base - 3] + outG[base + 3]) >> 1) * (dh <= dv) + + ((outG[base - sx3] + outG[base + sx3]) >> 1) * (dh > dv)); + CLIP16(tmp, outG[base], bits); + } + } + for (i3 = 3 * sx3; i3 < (sy - 3) * sx3; i3 += (sx3 << 1)) + { + for (j3 = 9; j3 < sx3 - 6; j3 += 6) + { + base = i3 + j3; + dh = abs(((outR[base - 6] + outR[base + 6]) >> 1) - outR[base]); + dv = abs(((outR[base - (sx3 << 1)] + outR[base + (sx3 << 1)]) >> 1) - outR[base]); + tmp = (((outG[base - 3] + outG[base + 3]) >> 1) * (dh <= dv) + + ((outG[base - sx3] + outG[base + sx3]) >> 1) * (dh > dv)); + CLIP16(tmp, outG[base], bits); + } + } + /* process RED channel */ + for (i3 = sx3; i3 < (sy - 1) * sx3; i3 += (sx3 << 1)) + { /* G-points (1/2) */ + for (j3 = 6; j3 < sx3 - 3; j3 += 6) + { + base = i3 + j3; + tmp = outG[base] + ((outR[base - 3] - outG[base - 3] + outR[base + 3] - outG[base + 3]) >> 1); + CLIP16(tmp, outR[base], bits); + } + } + for (i3 = 2 * sx3; i3 < (sy - 2) * sx3; i3 += (sx3 << 1)) + { + for (j3 = 3; j3 < sx3; j3 += 6) + { /* G-points (2/2) */ + base = i3 + j3; + tmp = + outG[base] + ((outR[base - sx3] - outG[base - sx3] + outR[base + sx3] - outG[base + sx3]) >> 1); + CLIP16(tmp, outR[base], bits); + } + for (j3 = 6; j3 < sx3 - 3; j3 += 6) + { /* B-points */ + base = i3 + j3; + tmp = outG[base] + ((outR[base - sx3 - 3] - outG[base - sx3 - 3] + outR[base - sx3 + 3] - + outG[base - sx3 + 3] + outR[base + sx3 - 3] - outG[base + sx3 - 3] + + outR[base + sx3 + 3] - outG[base + sx3 + 3]) >> + 2); + CLIP16(tmp, outR[base], bits); + } + } + + /* process BLUE channel */ + for (i = 0, i3 = 0; i < sy * sx; i += (sx << 1), i3 += (sx3 << 1)) + { + for (j = 1, j3 = 3; j < sx - 2; j += 2, j3 += 6) + { + base = i3 + j3; + tmp = outG[base] + ((outB[base - 3] - outG[base - 3] + outB[base + 3] - outG[base + 3]) >> 1); + CLIP16(tmp, outB[base], bits); + } + } + for (i3 = sx3; i3 < (sy - 1) * sx3; i3 += (sx3 << 1)) + { + for (j3 = 0; j3 < sx3 - 3; j3 += 6) + { + base = i3 + j3; + tmp = + outG[base] + ((outB[base - sx3] - outG[base - sx3] + outB[base + sx3] - outG[base + sx3]) >> 1); + CLIP16(tmp, outB[base], bits); + } + for (j3 = 3; j3 < sx3 - 6; j3 += 6) + { + base = i3 + j3; + tmp = outG[base] + ((outB[base - sx3 - 3] - outG[base - sx3 - 3] + outB[base - sx3 + 3] - + outG[base - sx3 + 3] + outB[base + sx3 - 3] - outG[base + sx3 - 3] + + outB[base + sx3 + 3] - outG[base + sx3 + 3]) >> + 2); + CLIP16(tmp, outB[base], bits); + } + } + break; + } + + ClearBorders_uint16(rgb, sx, sy, 3); + + return DC1394_SUCCESS; +} + +/* coriander's Bayer decoding */ +dc1394error_t dc1394_bayer_Downsample_uint16(const uint16_t *bayer, uint16_t *rgb, int sx, int sy, + int tile, int bits) +{ + uint16_t *outR, *outG, *outB; + register int i, j; + int tmp; + + switch (tile) + { + case DC1394_COLOR_FILTER_GRBG: + case DC1394_COLOR_FILTER_BGGR: + outR = &rgb[0]; + outG = &rgb[1]; + outB = &rgb[2]; + break; + case DC1394_COLOR_FILTER_GBRG: + case DC1394_COLOR_FILTER_RGGB: + outR = &rgb[2]; + outG = &rgb[1]; + outB = &rgb[0]; + break; + default: + return DC1394_INVALID_COLOR_FILTER; + } + + switch (tile) + { + case DC1394_COLOR_FILTER_GRBG: + case DC1394_COLOR_FILTER_GBRG: + for (i = 0; i < sy * sx; i += (sx << 1)) + { + for (j = 0; j < sx; j += 2) + { + tmp = ((bayer[i + j] + bayer[i + sx + j + 1]) >> 1); + CLIP16(tmp, outG[((i >> 2) + (j >> 1)) * 3], bits); + tmp = bayer[i + sx + j + 1]; + CLIP16(tmp, outR[((i >> 2) + (j >> 1)) * 3], bits); + tmp = bayer[i + sx + j]; + CLIP16(tmp, outB[((i >> 2) + (j >> 1)) * 3], bits); + } + } + break; + case DC1394_COLOR_FILTER_BGGR: + case DC1394_COLOR_FILTER_RGGB: + for (i = 0; i < sy * sx; i += (sx << 1)) + { + for (j = 0; j < sx; j += 2) + { + tmp = ((bayer[i + sx + j] + bayer[i + j + 1]) >> 1); + CLIP16(tmp, outG[((i >> 2) + (j >> 1)) * 3], bits); + tmp = bayer[i + sx + j + 1]; + CLIP16(tmp, outR[((i >> 2) + (j >> 1)) * 3], bits); + tmp = bayer[i + j]; + CLIP16(tmp, outB[((i >> 2) + (j >> 1)) * 3], bits); + } + } + break; + } + + return DC1394_SUCCESS; +} + +/* coriander's Bayer decoding */ +dc1394error_t dc1394_bayer_Simple_uint16(const uint16_t *bayer, uint16_t *rgb, int sx, int sy, + int tile, int bits) +{ + uint16_t *outR, *outG, *outB; + register int i, j; + int tmp, base; + + /* sx and sy should be even */ + switch (tile) + { + case DC1394_COLOR_FILTER_GRBG: + case DC1394_COLOR_FILTER_BGGR: + outR = &rgb[0]; + outG = &rgb[1]; + outB = &rgb[2]; + break; + case DC1394_COLOR_FILTER_GBRG: + case DC1394_COLOR_FILTER_RGGB: + outR = &rgb[2]; + outG = &rgb[1]; + outB = &rgb[0]; + break; + default: + return DC1394_INVALID_COLOR_FILTER; + } + + switch (tile) + { + case DC1394_COLOR_FILTER_GRBG: + case DC1394_COLOR_FILTER_BGGR: + outR = &rgb[0]; + outG = &rgb[1]; + outB = &rgb[2]; + break; + case DC1394_COLOR_FILTER_GBRG: + case DC1394_COLOR_FILTER_RGGB: + outR = &rgb[2]; + outG = &rgb[1]; + outB = &rgb[0]; + break; + default: + outR = NULL; + outG = NULL; + outB = NULL; + break; + } + + switch (tile) + { + case DC1394_COLOR_FILTER_GRBG: + case DC1394_COLOR_FILTER_GBRG: + for (i = 0; i < sy - 1; i += 2) + { + for (j = 0; j < sx - 1; j += 2) + { + base = i * sx + j; + tmp = ((bayer[base] + bayer[base + sx + 1]) >> 1); + CLIP16(tmp, outG[base * 3], bits); + tmp = bayer[base + 1]; + CLIP16(tmp, outR[base * 3], bits); + tmp = bayer[base + sx]; + CLIP16(tmp, outB[base * 3], bits); + } + } + for (i = 0; i < sy - 1; i += 2) + { + for (j = 1; j < sx - 1; j += 2) + { + base = i * sx + j; + tmp = ((bayer[base + 1] + bayer[base + sx]) >> 1); + CLIP16(tmp, outG[(base)*3], bits); + tmp = bayer[base]; + CLIP16(tmp, outR[(base)*3], bits); + tmp = bayer[base + 1 + sx]; + CLIP16(tmp, outB[(base)*3], bits); + } + } + for (i = 1; i < sy - 1; i += 2) + { + for (j = 0; j < sx - 1; j += 2) + { + base = i * sx + j; + tmp = ((bayer[base + sx] + bayer[base + 1]) >> 1); + CLIP16(tmp, outG[base * 3], bits); + tmp = bayer[base + sx + 1]; + CLIP16(tmp, outR[base * 3], bits); + tmp = bayer[base]; + CLIP16(tmp, outB[base * 3], bits); + } + } + for (i = 1; i < sy - 1; i += 2) + { + for (j = 1; j < sx - 1; j += 2) + { + base = i * sx + j; + tmp = ((bayer[base] + bayer[base + 1 + sx]) >> 1); + CLIP16(tmp, outG[(base)*3], bits); + tmp = bayer[base + sx]; + CLIP16(tmp, outR[(base)*3], bits); + tmp = bayer[base + 1]; + CLIP16(tmp, outB[(base)*3], bits); + } + } + break; + case DC1394_COLOR_FILTER_BGGR: + case DC1394_COLOR_FILTER_RGGB: + for (i = 0; i < sy - 1; i += 2) + { + for (j = 0; j < sx - 1; j += 2) + { + base = i * sx + j; + tmp = ((bayer[base + sx] + bayer[base + 1]) >> 1); + CLIP16(tmp, outG[base * 3], bits); + tmp = bayer[base + sx + 1]; + CLIP16(tmp, outR[base * 3], bits); + tmp = bayer[base]; + CLIP16(tmp, outB[base * 3], bits); + } + } + for (i = 1; i < sy - 1; i += 2) + { + for (j = 0; j < sx - 1; j += 2) + { + base = i * sx + j; + tmp = ((bayer[base] + bayer[base + 1 + sx]) >> 1); + CLIP16(tmp, outG[(base)*3], bits); + tmp = bayer[base + 1]; + CLIP16(tmp, outR[(base)*3], bits); + tmp = bayer[base + sx]; + CLIP16(tmp, outB[(base)*3], bits); + } + } + for (i = 0; i < sy - 1; i += 2) + { + for (j = 1; j < sx - 1; j += 2) + { + base = i * sx + j; + tmp = ((bayer[base] + bayer[base + sx + 1]) >> 1); + CLIP16(tmp, outG[base * 3], bits); + tmp = bayer[base + sx]; + CLIP16(tmp, outR[base * 3], bits); + tmp = bayer[base + 1]; + CLIP16(tmp, outB[base * 3], bits); + } + } + for (i = 1; i < sy - 1; i += 2) + { + for (j = 1; j < sx - 1; j += 2) + { + base = i * sx + j; + tmp = ((bayer[base + 1] + bayer[base + sx]) >> 1); + CLIP16(tmp, outG[(base)*3], bits); + tmp = bayer[base]; + CLIP16(tmp, outR[(base)*3], bits); + tmp = bayer[base + 1 + sx]; + CLIP16(tmp, outB[(base)*3], bits); + } + } + break; + } + + /* add black border */ + for (i = sx * (sy - 1) * 3; i < sx * sy * 3; i++) + { + rgb[i] = 0; + } + for (i = (sx - 1) * 3; i < sx * sy * 3; i += (sx - 1) * 3) + { + rgb[i++] = 0; + rgb[i++] = 0; + rgb[i++] = 0; + } + + return DC1394_SUCCESS; +} + +/* Variable Number of Gradients, from dcraw */ +/* Ported to libdc1394 by Frederic Devernay */ + +#define FORC3 for (c = 0; c < 3; c++) + +#define SQR(x) ((x) * (x)) +#define ABS(x) (((int)(x) ^ ((int)(x) >> 31)) - ((int)(x) >> 31)) +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif +#define LIM(x, min, max) MAX(min, MIN(x, max)) +#define ULIM(x, y, z) ((y) < (z) ? LIM(x, y, z) : LIM(x, z, y)) +/* + In order to inline this calculation, I make the risky + assumption that all filter patterns can be described + by a repeating pattern of eight rows and two columns + + Return values are either 0/1/2/3 = G/M/C/Y or 0/1/2/3 = R/G1/B/G2 + */ +#define FC(row, col) (filters >> ((((row) << 1 & 14) + ((col)&1)) << 1) & 3) + +/* + This algorithm is officially called: + + "Interpolation using a Threshold-based variable number of gradients" + + described in http://www-ise.stanford.edu/~tingchen/algodep/vargra.html + + I've extended the basic idea to work with non-Bayer filter arrays. + Gradients are numbered clockwise from NW=0 to W=7. + */ +static const signed char bayervng_terms[] = + { -2, -2, +0, -1, 0, 0x01, -2, -2, +0, +0, 1, 0x01, -2, -1, -1, +0, 0, 0x01, -2, -1, +0, -1, 0, 0x02, + -2, -1, +0, +0, 0, 0x03, -2, -1, +0, +1, 1, 0x01, -2, +0, +0, -1, 0, 0x06, -2, +0, +0, +0, 1, 0x02, + -2, +0, +0, +1, 0, 0x03, -2, +1, -1, +0, 0, 0x04, -2, +1, +0, -1, 1, 0x04, -2, +1, +0, +0, 0, 0x06, + -2, +1, +0, +1, 0, 0x02, -2, +2, +0, +0, 1, 0x04, -2, +2, +0, +1, 0, 0x04, -1, -2, -1, +0, 0, 0x80, + -1, -2, +0, -1, 0, 0x01, -1, -2, +1, -1, 0, 0x01, -1, -2, +1, +0, 1, 0x01, -1, -1, -1, +1, 0, 0x88, + -1, -1, +1, -2, 0, 0x40, -1, -1, +1, -1, 0, 0x22, -1, -1, +1, +0, 0, 0x33, -1, -1, +1, +1, 1, 0x11, + -1, +0, -1, +2, 0, 0x08, -1, +0, +0, -1, 0, 0x44, -1, +0, +0, +1, 0, 0x11, -1, +0, +1, -2, 1, 0x40, + -1, +0, +1, -1, 0, 0x66, -1, +0, +1, +0, 1, 0x22, -1, +0, +1, +1, 0, 0x33, -1, +0, +1, +2, 1, 0x10, + -1, +1, +1, -1, 1, 0x44, -1, +1, +1, +0, 0, 0x66, -1, +1, +1, +1, 0, 0x22, -1, +1, +1, +2, 0, 0x10, + -1, +2, +0, +1, 0, 0x04, -1, +2, +1, +0, 1, 0x04, -1, +2, +1, +1, 0, 0x04, +0, -2, +0, +0, 1, 0x80, + +0, -1, +0, +1, 1, 0x88, +0, -1, +1, -2, 0, 0x40, +0, -1, +1, +0, 0, 0x11, +0, -1, +2, -2, 0, 0x40, + +0, -1, +2, -1, 0, 0x20, +0, -1, +2, +0, 0, 0x30, +0, -1, +2, +1, 1, 0x10, +0, +0, +0, +2, 1, 0x08, + +0, +0, +2, -2, 1, 0x40, +0, +0, +2, -1, 0, 0x60, +0, +0, +2, +0, 1, 0x20, +0, +0, +2, +1, 0, 0x30, + +0, +0, +2, +2, 1, 0x10, +0, +1, +1, +0, 0, 0x44, +0, +1, +1, +2, 0, 0x10, +0, +1, +2, -1, 1, 0x40, + +0, +1, +2, +0, 0, 0x60, +0, +1, +2, +1, 0, 0x20, +0, +1, +2, +2, 0, 0x10, +1, -2, +1, +0, 0, 0x80, + +1, -1, +1, +1, 0, 0x88, +1, +0, +1, +2, 0, 0x08, +1, +0, +2, -1, 0, 0x40, +1, +0, +2, +1, 0, 0x10 }, + bayervng_chood[] = { -1, -1, -1, 0, -1, +1, 0, +1, +1, +1, +1, 0, +1, -1, 0, -1 }; + +dc1394error_t dc1394_bayer_VNG(const uint8_t *bayer, uint8_t *dst, int sx, int sy, + dc1394color_filter_t pattern) +{ + const int height = sy, width = sx; + static const signed char *cp; + /* the following has the same type as the image */ + uint8_t(*brow[5])[3], *pix; /* [FD] */ + int code[8][2][320], *ip, gval[8], gmin, gmax, sum[4]; + int row, col, x, y, x1, x2, y1, y2, t, weight, grads, color, diag; + int g, diff, thold, num, c; + uint32_t filters; /* [FD] */ + + /* first, use bilinear bayer decoding */ + dc1394_bayer_Bilinear(bayer, dst, sx, sy, pattern); + + switch (pattern) + { + case DC1394_COLOR_FILTER_BGGR: + filters = 0x16161616; + break; + case DC1394_COLOR_FILTER_GRBG: + filters = 0x61616161; + break; + case DC1394_COLOR_FILTER_RGGB: + filters = 0x94949494; + break; + case DC1394_COLOR_FILTER_GBRG: + filters = 0x49494949; + break; + default: + return DC1394_INVALID_COLOR_FILTER; + } + + for (row = 0; row < 8; row++) + { /* Precalculate for VNG */ + for (col = 0; col < 2; col++) + { + ip = code[row][col]; + for (cp = bayervng_terms, t = 0; t < 64; t++) + { + y1 = *cp++; + x1 = *cp++; + y2 = *cp++; + x2 = *cp++; + weight = *cp++; + grads = *cp++; + color = FC(row + y1, col + x1); + if ((int)FC(row + y2, col + x2) != color) + continue; + diag = ((int)FC(row, col + 1) == color && (int)FC(row + 1, col) == color) ? 2 : 1; + if (abs(y1 - y2) == diag && abs(x1 - x2) == diag) + continue; + *ip++ = (y1 * width + x1) * 3 + color; /* [FD] */ + *ip++ = (y2 * width + x2) * 3 + color; /* [FD] */ + *ip++ = weight; + for (g = 0; g < 8; g++) + if (grads & 1 << g) + *ip++ = g; + *ip++ = -1; + } + *ip++ = INT_MAX; + for (cp = bayervng_chood, g = 0; g < 8; g++) + { + y = *cp++; + x = *cp++; + *ip++ = (y * width + x) * 3; /* [FD] */ + color = FC(row, col); + if ((int)FC(row + y, col + x) != color && (int)FC(row + y * 2, col + x * 2) == color) + *ip++ = (y * width + x) * 6 + color; /* [FD] */ + else + *ip++ = 0; + } + } + } + brow[4] = calloc(width * 3, sizeof **brow); + for (row = 0; row < 3; row++) + brow[row] = brow[4] + row * width; + for (row = 2; row < height - 2; row++) + { /* Do VNG interpolation */ + for (col = 2; col < width - 2; col++) + { + pix = dst + (row * width + col) * 3; /* [FD] */ + ip = code[row & 7][col & 1]; + memset(gval, 0, sizeof gval); + while ((g = ip[0]) != INT_MAX) + { /* Calculate gradients */ + diff = ABS(pix[g] - pix[ip[1]]) << ip[2]; + gval[ip[3]] += diff; + ip += 5; + if ((g = ip[-1]) == -1) + continue; + gval[g] += diff; + while ((g = *ip++) != -1) + gval[g] += diff; + } + ip++; + gmin = gmax = gval[0]; /* Choose a threshold */ + for (g = 1; g < 8; g++) + { + if (gmin > gval[g]) + gmin = gval[g]; + if (gmax < gval[g]) + gmax = gval[g]; + } + if (gmax == 0) + { + memcpy(brow[2][col], pix, 3 * sizeof *dst); /* [FD] */ + continue; + } + thold = gmin + (gmax >> 1); + memset(sum, 0, sizeof sum); + color = FC(row, col); + for (num = g = 0; g < 8; g++, ip += 2) + { /* Average the neighbors */ + if (gval[g] <= thold) + { + for (c = 0; c < 3; c++) /* [FD] */ + if (c == color && ip[1]) + sum[c] += (pix[c] + pix[ip[1]]) >> 1; + else + sum[c] += pix[ip[0] + c]; + num++; + } + } + for (c = 0; c < 3; c++) + { /* [FD] Save to buffer */ + t = pix[color]; + if (c != color) + t += (sum[c] - sum[color]) / num; + CLIP(t, brow[2][col][c]); /* [FD] */ + } + } + if (row > 3) /* Write buffer to image */ + memcpy(dst + 3 * ((row - 2) * width + 2), brow[0] + 2, (width - 4) * 3 * sizeof *dst); /* [FD] */ + for (g = 0; g < 4; g++) + brow[(g - 1) & 3] = brow[g]; + } + memcpy(dst + 3 * ((row - 2) * width + 2), brow[0] + 2, (width - 4) * 3 * sizeof *dst); + memcpy(dst + 3 * ((row - 1) * width + 2), brow[1] + 2, (width - 4) * 3 * sizeof *dst); + free(brow[4]); + + return DC1394_SUCCESS; +} + +dc1394error_t dc1394_bayer_VNG_uint16(const uint16_t *bayer, uint16_t *dst, int sx, int sy, + dc1394color_filter_t pattern, int bits) +{ + const int height = sy, width = sx; + static const signed char *cp; + /* the following has the same type as the image */ + uint16_t(*brow[5])[3], *pix; /* [FD] */ + int code[8][2][320], *ip, gval[8], gmin, gmax, sum[4]; + int row, col, x, y, x1, x2, y1, y2, t, weight, grads, color, diag; + int g, diff, thold, num, c; + uint32_t filters; /* [FD] */ + + /* first, use bilinear bayer decoding */ + + dc1394_bayer_Bilinear_uint16(bayer, dst, sx, sy, pattern, bits); + + switch (pattern) + { + case DC1394_COLOR_FILTER_BGGR: + filters = 0x16161616; + break; + case DC1394_COLOR_FILTER_GRBG: + filters = 0x61616161; + break; + case DC1394_COLOR_FILTER_RGGB: + filters = 0x94949494; + break; + case DC1394_COLOR_FILTER_GBRG: + filters = 0x49494949; + break; + default: + return DC1394_INVALID_COLOR_FILTER; + } + + for (row = 0; row < 8; row++) + { /* Precalculate for VNG */ + for (col = 0; col < 2; col++) + { + ip = code[row][col]; + for (cp = bayervng_terms, t = 0; t < 64; t++) + { + y1 = *cp++; + x1 = *cp++; + y2 = *cp++; + x2 = *cp++; + weight = *cp++; + grads = *cp++; + color = FC(row + y1, col + x1); + if ((int)FC(row + y2, col + x2) != color) + continue; + diag = ((int)FC(row, col + 1) == color && (int)FC(row + 1, col) == color) ? 2 : 1; + if (abs(y1 - y2) == diag && abs(x1 - x2) == diag) + continue; + *ip++ = (y1 * width + x1) * 3 + color; /* [FD] */ + *ip++ = (y2 * width + x2) * 3 + color; /* [FD] */ + *ip++ = weight; + for (g = 0; g < 8; g++) + if (grads & 1 << g) + *ip++ = g; + *ip++ = -1; + } + *ip++ = INT_MAX; + for (cp = bayervng_chood, g = 0; g < 8; g++) + { + y = *cp++; + x = *cp++; + *ip++ = (y * width + x) * 3; /* [FD] */ + color = FC(row, col); + if ((int)FC(row + y, col + x) != color && (int)FC(row + y * 2, col + x * 2) == color) + *ip++ = (y * width + x) * 6 + color; /* [FD] */ + else + *ip++ = 0; + } + } + } + brow[4] = calloc(width * 3, sizeof **brow); + for (row = 0; row < 3; row++) + brow[row] = brow[4] + row * width; + for (row = 2; row < height - 2; row++) + { /* Do VNG interpolation */ + for (col = 2; col < width - 2; col++) + { + pix = dst + (row * width + col) * 3; /* [FD] */ + ip = code[row & 7][col & 1]; + memset(gval, 0, sizeof gval); + while ((g = ip[0]) != INT_MAX) + { /* Calculate gradients */ + diff = ABS(pix[g] - pix[ip[1]]) << ip[2]; + gval[ip[3]] += diff; + ip += 5; + if ((g = ip[-1]) == -1) + continue; + gval[g] += diff; + while ((g = *ip++) != -1) + gval[g] += diff; + } + ip++; + gmin = gmax = gval[0]; /* Choose a threshold */ + for (g = 1; g < 8; g++) + { + if (gmin > gval[g]) + gmin = gval[g]; + if (gmax < gval[g]) + gmax = gval[g]; + } + if (gmax == 0) + { + memcpy(brow[2][col], pix, 3 * sizeof *dst); /* [FD] */ + continue; + } + thold = gmin + (gmax >> 1); + memset(sum, 0, sizeof sum); + color = FC(row, col); + for (num = g = 0; g < 8; g++, ip += 2) + { /* Average the neighbors */ + if (gval[g] <= thold) + { + for (c = 0; c < 3; c++) /* [FD] */ + if (c == color && ip[1]) + sum[c] += (pix[c] + pix[ip[1]]) >> 1; + else + sum[c] += pix[ip[0] + c]; + num++; + } + } + for (c = 0; c < 3; c++) + { /* [FD] Save to buffer */ + t = pix[color]; + if (c != color) + t += (sum[c] - sum[color]) / num; + CLIP16(t, brow[2][col][c], bits); /* [FD] */ + } + } + if (row > 3) /* Write buffer to image */ + memcpy(dst + 3 * ((row - 2) * width + 2), brow[0] + 2, (width - 4) * 3 * sizeof *dst); /* [FD] */ + for (g = 0; g < 4; g++) + brow[(g - 1) & 3] = brow[g]; + } + memcpy(dst + 3 * ((row - 2) * width + 2), brow[0] + 2, (width - 4) * 3 * sizeof *dst); + memcpy(dst + 3 * ((row - 1) * width + 2), brow[1] + 2, (width - 4) * 3 * sizeof *dst); + free(brow[4]); + + return DC1394_SUCCESS; +} + +/* AHD interpolation ported from dcraw to libdc1394 by Samuel Audet */ +static dc1394bool_t ahd_inited = DC1394_FALSE; /* WARNING: not multi-processor safe */ + +#define CLIPOUT(x) LIM(x, 0, 255) +#define CLIPOUT16(x, bits) LIM(x, 0, ((1 << bits) - 1)) + +static const double xyz_rgb[3][3] = { /* XYZ from RGB */ + { 0.412453, 0.357580, 0.180423 }, + { 0.212671, 0.715160, 0.072169 }, + { 0.019334, 0.119193, 0.950227 } +}; +static const float d65_white[3] = { 0.950456, 1, 1.088754 }; + +static void cam_to_cielab(uint16_t cam[3], float lab[3]) /* [SA] */ +{ + int c, i, j; + float r, xyz[3]; + static float cbrt[0x10000], xyz_cam[3][4]; + + if (cam == NULL) + { + for (i = 0; i < 0x10000; i++) + { + r = i / 65535.0; + cbrt[i] = r > 0.008856 ? pow(r, 1 / 3.0) : 7.787 * r + 16 / 116.0; + } + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) /* [SA] */ + xyz_cam[i][j] = xyz_rgb[i][j] / d65_white[i]; /* [SA] */ + } + else + { + xyz[0] = xyz[1] = xyz[2] = 0.5; + FORC3 + { /* [SA] */ + xyz[0] += xyz_cam[0][c] * cam[c]; + xyz[1] += xyz_cam[1][c] * cam[c]; + xyz[2] += xyz_cam[2][c] * cam[c]; + } + xyz[0] = cbrt[CLIPOUT16((int)xyz[0], 16)]; /* [SA] */ + xyz[1] = cbrt[CLIPOUT16((int)xyz[1], 16)]; /* [SA] */ + xyz[2] = cbrt[CLIPOUT16((int)xyz[2], 16)]; /* [SA] */ + lab[0] = 116 * xyz[1] - 16; + lab[1] = 500 * (xyz[0] - xyz[1]); + lab[2] = 200 * (xyz[1] - xyz[2]); + } +} + +/* + Adaptive Homogeneity-Directed interpolation is based on + the work of Keigo Hirakawa, Thomas Parks, and Paul Lee. + */ +#define TS 256 /* Tile Size */ + +dc1394error_t dc1394_bayer_AHD(const uint8_t *bayer, uint8_t *dst, int sx, int sy, + dc1394color_filter_t pattern) +{ + int i, j, top, left, row, col, tr, tc, fc, c, d, val, hm[2]; + /* the following has the same type as the image */ + uint8_t(*pix)[3], (*rix)[3]; /* [SA] */ + uint16_t rix16[3]; /* [SA] */ + static const int dir[4] = { -1, 1, -TS, TS }; + unsigned ldiff[2][4], abdiff[2][4], leps, abeps; + float flab[3]; /* [SA] */ + uint8_t(*rgb)[TS][TS][3]; + short(*lab)[TS][TS][3]; + char(*homo)[TS][TS], *buffer; + + /* start - new code for libdc1394 */ + uint32_t filters; + const int height = sy, width = sx; + int x, y; + + if (ahd_inited == DC1394_FALSE) + { + /* WARNING: this might not be multi-processor safe */ + cam_to_cielab(NULL, NULL); + ahd_inited = DC1394_TRUE; + } + + switch (pattern) + { + case DC1394_COLOR_FILTER_BGGR: + filters = 0x16161616; + break; + case DC1394_COLOR_FILTER_GRBG: + filters = 0x61616161; + break; + case DC1394_COLOR_FILTER_RGGB: + filters = 0x94949494; + break; + case DC1394_COLOR_FILTER_GBRG: + filters = 0x49494949; + break; + default: + return DC1394_INVALID_COLOR_FILTER; + } + + /* fill-in destination with known exact values */ + for (y = 0; y < height; y++) + { + for (x = 0; x < width; x++) + { + int channel = FC(y, x); + dst[(y * width + x) * 3 + channel] = bayer[y * width + x]; + } + } + /* end - new code for libdc1394 */ + + /* start - code from border_interpolate (int border) */ + { + int border = 3; + int row, col, y, x; + unsigned f, c, sum[8]; + + for (row = 0; row < height; row++) + for (col = 0; col < width; col++) + { + if (col == border && row >= border && row < height - border) + col = width - border; + memset(sum, 0, sizeof sum); + for (y = row - 1; y != row + 2; y++) + for (x = col - 1; x != col + 2; x++) + if (y < height && x < width) + { + f = FC(y, x); + sum[f] += dst[(y * width + x) * 3 + f]; /* [SA] */ + sum[f + 4]++; + } + f = FC(row, col); + FORC3 if (c != f && sum[c + 4]) /* [SA] */ + dst[(row * width + col) * 3 + c] = sum[c] / sum[c + 4]; /* [SA] */ + } + } + /* end - code from border_interpolate (int border) */ + + buffer = (char *)malloc(26 * TS * TS); /* 1664 kB */ + /* merror (buffer, "ahd_interpolate()"); */ + rgb = (uint8_t(*)[TS][TS][3])buffer; /* [SA] */ + lab = (short(*)[TS][TS][3])(buffer + 12 * TS * TS); + homo = (char(*)[TS][TS])(buffer + 24 * TS * TS); + + for (top = 0; top < height; top += TS - 6) + for (left = 0; left < width; left += TS - 6) + { + memset(rgb, 0, 12 * TS * TS); + + /* Interpolate green horizontally and vertically: */ + for (row = top < 2 ? 2 : top; row < top + TS && row < height - 2; row++) + { + col = left + (FC(row, left) == 1); + if (col < 2) + col += 2; + for (fc = FC(row, col); col < left + TS && col < width - 2; col += 2) + { + pix = (uint8_t(*)[3])dst + (row * width + col); /* [SA] */ + val = ((pix[-1][1] + pix[0][fc] + pix[1][1]) * 2 - pix[-2][fc] - pix[2][fc]) >> 2; + rgb[0][row - top][col - left][1] = ULIM(val, pix[-1][1], pix[1][1]); + val = ((pix[-width][1] + pix[0][fc] + pix[width][1]) * 2 - pix[-2 * width][fc] - + pix[2 * width][fc]) >> + 2; + rgb[1][row - top][col - left][1] = ULIM(val, pix[-width][1], pix[width][1]); + } + } + /* Interpolate red and blue, and convert to CIELab: */ + for (d = 0; d < 2; d++) + for (row = top + 1; row < top + TS - 1 && row < height - 1; row++) + for (col = left + 1; col < left + TS - 1 && col < width - 1; col++) + { + pix = (uint8_t(*)[3])dst + (row * width + col); /* [SA] */ + rix = &rgb[d][row - top][col - left]; + if ((c = 2 - FC(row, col)) == 1) + { + c = FC(row + 1, col); + val = pix[0][1] + ((pix[-1][2 - c] + pix[1][2 - c] - rix[-1][1] - rix[1][1]) >> 1); + rix[0][2 - c] = CLIPOUT(val); /* [SA] */ + val = pix[0][1] + ((pix[-width][c] + pix[width][c] - rix[-TS][1] - rix[TS][1]) >> 1); + } + else + val = rix[0][1] + + ((pix[-width - 1][c] + pix[-width + 1][c] + pix[+width - 1][c] + pix[+width + 1][c] - + rix[-TS - 1][1] - rix[-TS + 1][1] - rix[+TS - 1][1] - rix[+TS + 1][1] + 1) >> + 2); + rix[0][c] = CLIPOUT(val); /* [SA] */ + c = FC(row, col); + rix[0][c] = pix[0][c]; + rix16[0] = rix[0][0]; /* [SA] */ + rix16[1] = rix[0][1]; /* [SA] */ + rix16[2] = rix[0][2]; /* [SA] */ + cam_to_cielab(rix16, flab); /* [SA] */ + FORC3 lab[d][row - top][col - left][c] = 64 * flab[c]; + } + /* Build homogeneity maps from the CIELab images: */ + memset(homo, 0, 2 * TS * TS); + for (row = top + 2; row < top + TS - 2 && row < height; row++) + { + tr = row - top; + for (col = left + 2; col < left + TS - 2 && col < width; col++) + { + tc = col - left; + for (d = 0; d < 2; d++) + for (i = 0; i < 4; i++) + ldiff[d][i] = ABS(lab[d][tr][tc][0] - lab[d][tr][tc + dir[i]][0]); + leps = MIN(MAX(ldiff[0][0], ldiff[0][1]), MAX(ldiff[1][2], ldiff[1][3])); + for (d = 0; d < 2; d++) + for (i = 0; i < 4; i++) + if (i >> 1 == d || ldiff[d][i] <= leps) + abdiff[d][i] = SQR(lab[d][tr][tc][1] - lab[d][tr][tc + dir[i]][1]) + + SQR(lab[d][tr][tc][2] - lab[d][tr][tc + dir[i]][2]); + abeps = MIN(MAX(abdiff[0][0], abdiff[0][1]), MAX(abdiff[1][2], abdiff[1][3])); + for (d = 0; d < 2; d++) + for (i = 0; i < 4; i++) + if (ldiff[d][i] <= leps && abdiff[d][i] <= abeps) + homo[d][tr][tc]++; + } + } + /* Combine the most homogenous pixels for the final result: */ + for (row = top + 3; row < top + TS - 3 && row < height - 3; row++) + { + tr = row - top; + for (col = left + 3; col < left + TS - 3 && col < width - 3; col++) + { + tc = col - left; + for (d = 0; d < 2; d++) + for (hm[d] = 0, i = tr - 1; i <= tr + 1; i++) + for (j = tc - 1; j <= tc + 1; j++) + hm[d] += homo[d][i][j]; + if (hm[0] != hm[1]) + FORC3 dst[(row * width + col) * 3 + c] = CLIPOUT(rgb[hm[1] > hm[0]][tr][tc][c]); /* [SA] */ + else + FORC3 dst[(row * width + col) * 3 + c] = + CLIPOUT((rgb[0][tr][tc][c] + rgb[1][tr][tc][c]) >> 1); /* [SA] */ + } + } + } + free(buffer); + + return DC1394_SUCCESS; +} + +dc1394error_t dc1394_bayer_AHD_uint16(const uint16_t *bayer, uint16_t *dst, int sx, int sy, + dc1394color_filter_t pattern, int bits) +{ + int i, j, top, left, row, col, tr, tc, fc, c, d, val, hm[2]; + /* the following has the same type as the image */ + uint16_t(*pix)[3], (*rix)[3]; /* [SA] */ + static const int dir[4] = { -1, 1, -TS, TS }; + unsigned ldiff[2][4], abdiff[2][4], leps, abeps; + float flab[3]; + uint16_t(*rgb)[TS][TS][3]; /* [SA] */ + short(*lab)[TS][TS][3]; + char(*homo)[TS][TS], *buffer; + + /* start - new code for libdc1394 */ + uint32_t filters; + const int height = sy, width = sx; + int x, y; + + if (ahd_inited == DC1394_FALSE) + { + /* WARNING: this might not be multi-processor safe */ + cam_to_cielab(NULL, NULL); + ahd_inited = DC1394_TRUE; + } + + switch (pattern) + { + case DC1394_COLOR_FILTER_BGGR: + filters = 0x16161616; + break; + case DC1394_COLOR_FILTER_GRBG: + filters = 0x61616161; + break; + case DC1394_COLOR_FILTER_RGGB: + filters = 0x94949494; + break; + case DC1394_COLOR_FILTER_GBRG: + filters = 0x49494949; + break; + default: + return DC1394_INVALID_COLOR_FILTER; + } + + /* fill-in destination with known exact values */ + for (y = 0; y < height; y++) + { + for (x = 0; x < width; x++) + { + int channel = FC(y, x); + dst[(y * width + x) * 3 + channel] = bayer[y * width + x]; + } + } + /* end - new code for libdc1394 */ + + /* start - code from border_interpolate(int border) */ + { + int border = 3; + int row, col, y, x; + unsigned f, c, sum[8]; + + for (row = 0; row < height; row++) + for (col = 0; col < width; col++) + { + if (col == border && row >= border && row < height - border) + col = width - border; + memset(sum, 0, sizeof sum); + for (y = row - 1; y != row + 2; y++) + for (x = col - 1; x != col + 2; x++) + if (y < height && x < width) + { + f = FC(y, x); + sum[f] += dst[(y * width + x) * 3 + f]; /* [SA] */ + sum[f + 4]++; + } + f = FC(row, col); + FORC3 if (c != f && sum[c + 4]) /* [SA] */ + dst[(row * width + col) * 3 + c] = sum[c] / sum[c + 4]; /* [SA] */ + } + } + /* end - code from border_interpolate(int border) */ + + buffer = (char *)malloc(26 * TS * TS); /* 1664 kB */ + /* merror (buffer, "ahd_interpolate()"); */ + rgb = (uint16_t(*)[TS][TS][3])buffer; /* [SA] */ + lab = (short(*)[TS][TS][3])(buffer + 12 * TS * TS); + homo = (char(*)[TS][TS])(buffer + 24 * TS * TS); + + for (top = 0; top < height; top += TS - 6) + for (left = 0; left < width; left += TS - 6) + { + memset(rgb, 0, 12 * TS * TS); + + /* Interpolate green horizontally and vertically: */ + for (row = top < 2 ? 2 : top; row < top + TS && row < height - 2; row++) + { + col = left + (FC(row, left) == 1); + if (col < 2) + col += 2; + for (fc = FC(row, col); col < left + TS && col < width - 2; col += 2) + { + pix = (uint16_t(*)[3])dst + (row * width + col); /* [SA] */ + val = ((pix[-1][1] + pix[0][fc] + pix[1][1]) * 2 - pix[-2][fc] - pix[2][fc]) >> 2; + rgb[0][row - top][col - left][1] = ULIM(val, pix[-1][1], pix[1][1]); + val = ((pix[-width][1] + pix[0][fc] + pix[width][1]) * 2 - pix[-2 * width][fc] - + pix[2 * width][fc]) >> + 2; + rgb[1][row - top][col - left][1] = ULIM(val, pix[-width][1], pix[width][1]); + } + } + /* Interpolate red and blue, and convert to CIELab: */ + for (d = 0; d < 2; d++) + for (row = top + 1; row < top + TS - 1 && row < height - 1; row++) + for (col = left + 1; col < left + TS - 1 && col < width - 1; col++) + { + pix = (uint16_t(*)[3])dst + (row * width + col); /* [SA] */ + rix = &rgb[d][row - top][col - left]; + if ((c = 2 - FC(row, col)) == 1) + { + c = FC(row + 1, col); + val = pix[0][1] + ((pix[-1][2 - c] + pix[1][2 - c] - rix[-1][1] - rix[1][1]) >> 1); + rix[0][2 - c] = CLIPOUT16(val, bits); /* [SA] */ + val = pix[0][1] + ((pix[-width][c] + pix[width][c] - rix[-TS][1] - rix[TS][1]) >> 1); + } + else + val = rix[0][1] + + ((pix[-width - 1][c] + pix[-width + 1][c] + pix[+width - 1][c] + pix[+width + 1][c] - + rix[-TS - 1][1] - rix[-TS + 1][1] - rix[+TS - 1][1] - rix[+TS + 1][1] + 1) >> + 2); + rix[0][c] = CLIPOUT16(val, bits); /* [SA] */ + c = FC(row, col); + rix[0][c] = pix[0][c]; + cam_to_cielab(rix[0], flab); + FORC3 lab[d][row - top][col - left][c] = 64 * flab[c]; + } + /* Build homogeneity maps from the CIELab images: */ + memset(homo, 0, 2 * TS * TS); + for (row = top + 2; row < top + TS - 2 && row < height; row++) + { + tr = row - top; + for (col = left + 2; col < left + TS - 2 && col < width; col++) + { + tc = col - left; + for (d = 0; d < 2; d++) + for (i = 0; i < 4; i++) + ldiff[d][i] = ABS(lab[d][tr][tc][0] - lab[d][tr][tc + dir[i]][0]); + leps = MIN(MAX(ldiff[0][0], ldiff[0][1]), MAX(ldiff[1][2], ldiff[1][3])); + for (d = 0; d < 2; d++) + for (i = 0; i < 4; i++) + if (i >> 1 == d || ldiff[d][i] <= leps) + abdiff[d][i] = SQR(lab[d][tr][tc][1] - lab[d][tr][tc + dir[i]][1]) + + SQR(lab[d][tr][tc][2] - lab[d][tr][tc + dir[i]][2]); + abeps = MIN(MAX(abdiff[0][0], abdiff[0][1]), MAX(abdiff[1][2], abdiff[1][3])); + for (d = 0; d < 2; d++) + for (i = 0; i < 4; i++) + if (ldiff[d][i] <= leps && abdiff[d][i] <= abeps) + homo[d][tr][tc]++; + } + } + /* Combine the most homogenous pixels for the final result: */ + for (row = top + 3; row < top + TS - 3 && row < height - 3; row++) + { + tr = row - top; + for (col = left + 3; col < left + TS - 3 && col < width - 3; col++) + { + tc = col - left; + for (d = 0; d < 2; d++) + for (hm[d] = 0, i = tr - 1; i <= tr + 1; i++) + for (j = tc - 1; j <= tc + 1; j++) + hm[d] += homo[d][i][j]; + if (hm[0] != hm[1]) + FORC3 dst[(row * width + col) * 3 + c] = + CLIPOUT16(rgb[hm[1] > hm[0]][tr][tc][c], bits); /* [SA] */ + else + FORC3 dst[(row * width + col) * 3 + c] = + CLIPOUT16((rgb[0][tr][tc][c] + rgb[1][tr][tc][c]) >> 1, bits); /* [SA] */ + } + } + } + free(buffer); + + return DC1394_SUCCESS; +} + +dc1394error_t dc1394_bayer_decoding_8bit(const uint8_t *bayer, uint8_t *rgb, uint32_t sx, uint32_t sy, + dc1394color_filter_t tile, dc1394bayer_method_t method) +{ + switch (method) + { + case DC1394_BAYER_METHOD_NEAREST: + return dc1394_bayer_NearestNeighbor(bayer, rgb, sx, sy, tile); + case DC1394_BAYER_METHOD_SIMPLE: + return dc1394_bayer_Simple(bayer, rgb, sx, sy, tile); + case DC1394_BAYER_METHOD_BILINEAR: + return dc1394_bayer_Bilinear(bayer, rgb, sx, sy, tile); + case DC1394_BAYER_METHOD_HQLINEAR: + return dc1394_bayer_HQLinear(bayer, rgb, sx, sy, tile); + case DC1394_BAYER_METHOD_DOWNSAMPLE: + return dc1394_bayer_Downsample(bayer, rgb, sx, sy, tile); + case DC1394_BAYER_METHOD_EDGESENSE: + return dc1394_bayer_EdgeSense(bayer, rgb, sx, sy, tile); + case DC1394_BAYER_METHOD_VNG: + return dc1394_bayer_VNG(bayer, rgb, sx, sy, tile); + case DC1394_BAYER_METHOD_AHD: + return dc1394_bayer_AHD(bayer, rgb, sx, sy, tile); + default: + return DC1394_INVALID_BAYER_METHOD; + } +} + +dc1394error_t dc1394_bayer_decoding_16bit(const uint16_t *bayer, uint16_t *rgb, uint32_t sx, + uint32_t sy, dc1394color_filter_t tile, dc1394bayer_method_t method, + uint32_t bits) +{ + switch (method) + { + case DC1394_BAYER_METHOD_NEAREST: + return dc1394_bayer_NearestNeighbor_uint16(bayer, rgb, sx, sy, tile, bits); + case DC1394_BAYER_METHOD_SIMPLE: + return dc1394_bayer_Simple_uint16(bayer, rgb, sx, sy, tile, bits); + case DC1394_BAYER_METHOD_BILINEAR: + return dc1394_bayer_Bilinear_uint16(bayer, rgb, sx, sy, tile, bits); + case DC1394_BAYER_METHOD_HQLINEAR: + return dc1394_bayer_HQLinear_uint16(bayer, rgb, sx, sy, tile, bits); + case DC1394_BAYER_METHOD_DOWNSAMPLE: + return dc1394_bayer_Downsample_uint16(bayer, rgb, sx, sy, tile, bits); + case DC1394_BAYER_METHOD_EDGESENSE: + return dc1394_bayer_EdgeSense_uint16(bayer, rgb, sx, sy, tile, bits); + case DC1394_BAYER_METHOD_VNG: + return dc1394_bayer_VNG_uint16(bayer, rgb, sx, sy, tile, bits); + case DC1394_BAYER_METHOD_AHD: + return dc1394_bayer_AHD_uint16(bayer, rgb, sx, sy, tile, bits); + default: + return DC1394_INVALID_BAYER_METHOD; + } +} Index: lib/fitsformat/fitsdata.h =================================================================== --- /dev/null +++ lib/fitsformat/fitsdata.h @@ -0,0 +1,135 @@ +/*************************************************************************** +Gwenview: an image viewer + + fitsimage.cpp - FITS Image + ------------------- + begin : Tue Feb 24 2004 + copyright : (C) 2004 by Jasem Mutlaq + copyright : (C) 2017 by Csaba Kertesz + email : mutlaqja@ikarustech.com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * Some code fragments were adapted from Peter Kirchgessner's FITS plugin* + * See http://members.aol.com/pkirchg for more details. * + ***************************************************************************/ + +#pragma once + +#include "bayer.h" + +typedef enum { FITS_NORMAL, FITS_FOCUS, FITS_GUIDE, FITS_CALIBRATE, FITS_ALIGN } FITSMode; + +#ifdef WIN32 +// This header must be included before fitsio.h to avoid compiler errors with Visual Studio +#include +#endif + +#include + +#include +#include +#include + +class QProgressDialog; + +class FITSData +{ + public: + FITSData(); + ~FITSData(); + + /* Loads FITS image, scales it, and displays it in the GUI */ + bool loadFITS(const QString &filename); + /* Calculate stats */ + void calculateStats(bool refresh = false); + + // Access functions + void clearImageBuffers(); + uint8_t *getImageBuffer(); + + int getDataType() { return data_type; } + + // Stats + unsigned int getSize() { return stats.samples_per_channel; } + uint16_t getWidth() { return stats.width; } + uint16_t getHeight() { return stats.height; } + + // Statistics + int getNumOfChannels() { return channels; } + void getMinMax(double *min, double *max, uint8_t channel = 0) + { + *min = stats.min[channel]; + *max = stats.max[channel]; + } + + // Debayer + bool debayer(); + bool debayer_8bit(); + bool debayer_16bit(); + + // FITS Record + int getFITSRecord(QString &recordList, int &nkeys); + + // Create autostretch image from FITS File + static QImage FITSToImage(const QString &filename); + + QString getLastError() const; + + private: + int calculateMinMax(bool refresh = false); + bool checkDebayer(); + + // Templated functions + template + bool debayer(); + + template + void calculateMinMax(); + /* Calculate running average & standard deviation using Welford’s method for computing variance */ + template + void runningAverageStdDev(); + + /// Pointer to CFITSIO FITS file struct + fitsfile *fptr { nullptr }; + + /// FITS image data type (TBYTE, TUSHORT, TINT, TFLOAT, TLONG, TDOUBLE) + int data_type { 0 }; + /// Number of channels + int channels { 1 }; + /// Generic data image buffer + uint8_t *imageBuffer { nullptr }; + + /// Our very own file name + QString filename; + /// FITS Mode (Normal, WCS, Guide, Focus..etc) + FITSMode mode; + + uint8_t *bayerBuffer { nullptr }; + /// Bayer parameters + BayerParams debayerParams; + + /// Stats struct to hold statisical data about the FITS data + struct + { + double min[3], max[3]; + double mean[3]; + double stddev[3]; + double median[3]; + double SNR { 0 }; + int bitpix { 8 }; + int bytesPerPixel { 1 }; + int ndim { 2 }; + uint32_t samples_per_channel { 0 }; + uint16_t width { 0 }; + uint16_t height { 0 }; + } stats; + + QString lastError; +}; Index: lib/fitsformat/fitsdata.cpp =================================================================== --- /dev/null +++ lib/fitsformat/fitsdata.cpp @@ -0,0 +1,708 @@ +/*************************************************************************** +Gwenview: an image viewer + + fitsimage.cpp - FITS Image + ------------------- + begin : Tue Feb 24 2004 + copyright : (C) 2004 by Jasem Mutlaq + copyright : (C) 2017 by Csaba Kertesz + email : mutlaqja@ikarustech.com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * Some code fragments were adapted from Peter Kirchgessner's FITS plugin* + * See http://members.aol.com/pkirchg for more details. * + ***************************************************************************/ + +#include "fitsdata.h" + +#include +#include + +#include + +FITSData::FITSData() +{ + mode = FITS_NORMAL; + debayerParams.method = DC1394_BAYER_METHOD_NEAREST; + debayerParams.filter = DC1394_COLOR_FILTER_RGGB; + debayerParams.offsetX = debayerParams.offsetY = 0; +} + +FITSData::~FITSData() +{ + int status = 0; + + clearImageBuffers(); + + if (fptr) { + fits_close_file(fptr, &status); + } +} + +bool FITSData::loadFITS(const QString &inFilename) +{ + int status = 0, anynull = 0; + long naxes[3]; + char error_status[512]; + QString errMessage; + + if (fptr) { + fits_close_file(fptr, &status); + } + + filename = inFilename; + + if (fits_open_image(&fptr, filename.toLatin1(), READONLY, &status)) { + fits_report_error(stderr, status); + fits_get_errstatus(status, error_status); + errMessage = QString("Could not open file %1. Error %2").arg(filename, QString::fromUtf8(error_status)); + return false; + } + + if (fits_get_img_param(fptr, 3, &(stats.bitpix), &(stats.ndim), naxes, &status)) { + fits_report_error(stderr, status); + fits_get_errstatus(status, error_status); + errMessage = QString("FITS file open error (fits_get_img_param): %1").arg(QString::fromUtf8(error_status)); + return false; + } + + if (stats.ndim < 2) { + errMessage = "1D FITS images are not supported."; + return false; + } + + switch (stats.bitpix) + { + case BYTE_IMG: + data_type = TBYTE; + stats.bytesPerPixel = sizeof(uint8_t); + break; + case SHORT_IMG: + // Read SHORT image as USHORT + data_type = TUSHORT; + stats.bytesPerPixel = sizeof(int16_t); + break; + case USHORT_IMG: + data_type = TUSHORT; + stats.bytesPerPixel = sizeof(uint16_t); + break; + case LONG_IMG: + // Read LONG image as ULONG + data_type = TULONG; + stats.bytesPerPixel = sizeof(int32_t); + break; + case ULONG_IMG: + data_type = TULONG; + stats.bytesPerPixel = sizeof(uint32_t); + break; + case FLOAT_IMG: + data_type = TFLOAT; + stats.bytesPerPixel = sizeof(float); + break; + case LONGLONG_IMG: + data_type = TLONGLONG; + stats.bytesPerPixel = sizeof(int64_t); + break; + case DOUBLE_IMG: + data_type = TDOUBLE; + stats.bytesPerPixel = sizeof(double); + break; + default: + errMessage = QString("Bit depth %1 is not supported.").arg(stats.bitpix); + return false; + break; + } + + if (stats.ndim < 3) + naxes[2] = 1; + + if (naxes[0] == 0 || naxes[1] == 0) { + errMessage = QString("Image has invalid dimensions %1x%2").arg(naxes[0], naxes[1]); + return false; + } + + stats.width = naxes[0]; + stats.height = naxes[1]; + stats.samples_per_channel = stats.width * stats.height; + + clearImageBuffers(); + + channels = naxes[2]; + + imageBuffer = new uint8_t[stats.samples_per_channel * channels * stats.bytesPerPixel]; + + long nelements = stats.samples_per_channel * channels; + + if (fits_read_img(fptr, data_type, 1, nelements, 0, imageBuffer, &anynull, &status)) { + char errmsg[512]; + fits_get_errstatus(status, errmsg); + errMessage = QString("Error reading image: %1").arg(errmsg); + fits_report_error(stderr, status); + return false; + } + + calculateStats(); + + if (checkDebayer()) + { + bayerBuffer = imageBuffer; + debayer(); + } + return true; +} + +void FITSData::clearImageBuffers() +{ + delete[] imageBuffer; + imageBuffer = nullptr; + bayerBuffer = nullptr; +} + +void FITSData::calculateStats(bool refresh) +{ + // Calculate min max + calculateMinMax(refresh); + + // Get standard deviation and mean in one run + switch (data_type) + { + case TBYTE: + runningAverageStdDev(); + break; + + case TSHORT: + runningAverageStdDev(); + break; + + case TUSHORT: + runningAverageStdDev(); + break; + + case TLONG: + runningAverageStdDev(); + break; + + case TULONG: + runningAverageStdDev(); + break; + + case TFLOAT: + runningAverageStdDev(); + break; + + case TLONGLONG: + runningAverageStdDev(); + break; + + case TDOUBLE: + runningAverageStdDev(); + break; + + default: + return; + } + + stats.SNR = stats.mean[0] / stats.stddev[0]; +} + +int FITSData::calculateMinMax(bool refresh) +{ + int status, nfound = 0; + + status = 0; + + if (fptr && refresh == false) + { + if (fits_read_key_dbl(fptr, "DATAMIN", &(stats.min[0]), nullptr, &status) == 0) + nfound++; + + if (fits_read_key_dbl(fptr, "DATAMAX", &(stats.max[0]), nullptr, &status) == 0) + nfound++; + + // If we found both keywords, no need to calculate them, unless they are both zeros + if (nfound == 2 && !(stats.min[0] == 0 && stats.max[0] == 0)) + return 0; + } + + stats.min[0] = 1.0E30; + stats.max[0] = -1.0E30; + + stats.min[1] = 1.0E30; + stats.max[1] = -1.0E30; + + stats.min[2] = 1.0E30; + stats.max[2] = -1.0E30; + + switch (data_type) + { + case TBYTE: + calculateMinMax(); + break; + + case TSHORT: + calculateMinMax(); + break; + + case TUSHORT: + calculateMinMax(); + break; + + case TLONG: + calculateMinMax(); + break; + + case TULONG: + calculateMinMax(); + break; + + case TFLOAT: + calculateMinMax(); + break; + + case TLONGLONG: + calculateMinMax(); + break; + + case TDOUBLE: + calculateMinMax(); + break; + + default: + break; + } + + //qDebug() << "DATAMIN: " << stats.min << " - DATAMAX: " << stats.max; + return 0; +} + +template +void FITSData::calculateMinMax() +{ + T *buffer = reinterpret_cast(imageBuffer); + + if (channels == 1) + { + for (unsigned int i = 0; i < stats.samples_per_channel; i++) + { + if (buffer[i] < stats.min[0]) + stats.min[0] = buffer[i]; + else if (buffer[i] > stats.max[0]) + stats.max[0] = buffer[i]; + } + } + else + { + int g_offset = stats.samples_per_channel; + int b_offset = stats.samples_per_channel * 2; + + for (unsigned int i = 0; i < stats.samples_per_channel; i++) + { + if (buffer[i] < stats.min[0]) + stats.min[0] = buffer[i]; + else if (buffer[i] > stats.max[0]) + stats.max[0] = buffer[i]; + + if (buffer[i + g_offset] < stats.min[1]) + stats.min[1] = buffer[i + g_offset]; + else if (buffer[i + g_offset] > stats.max[1]) + stats.max[1] = buffer[i + g_offset]; + + if (buffer[i + b_offset] < stats.min[2]) + stats.min[2] = buffer[i + b_offset]; + else if (buffer[i + b_offset] > stats.max[2]) + stats.max[2] = buffer[i + b_offset]; + } + } +} + +template +void FITSData::runningAverageStdDev() +{ + T *buffer = reinterpret_cast(imageBuffer); + int m_n = 2; + double m_oldM = 0, m_newM = 0, m_oldS = 0, m_newS = 0; + + m_oldM = m_newM = buffer[0]; + for (unsigned int i = 1; i < stats.samples_per_channel; i++) + { + m_newM = m_oldM + (buffer[i] - m_oldM) / m_n; + m_newS = m_oldS + (buffer[i] - m_oldM) * (buffer[i] - m_newM); + + m_oldM = m_newM; + m_oldS = m_newS; + m_n++; + } + + double variance = (m_n == 2 ? 0 : m_newS / (m_n - 2)); + + stats.mean[0] = m_newM; + stats.stddev[0] = sqrt(variance); +} + +int FITSData::getFITSRecord(QString &recordList, int &nkeys) +{ + char *header = nullptr; + int status = 0; + + if (fits_hdr2str(fptr, 0, nullptr, 0, &header, &nkeys, &status)) + { + fits_report_error(stderr, status); + free(header); + return -1; + } + + recordList = QString(header); + + free(header); + + return 0; +} + +uint8_t *FITSData::getImageBuffer() +{ + return imageBuffer; +} + +bool FITSData::checkDebayer() +{ + int status = 0; + char bayerPattern[64]; + + // Let's search for BAYERPAT keyword, if it's not found we return as there is no bayer pattern in this image + if (fits_read_keyword(fptr, "BAYERPAT", bayerPattern, nullptr, &status)) + return false; + + if (stats.bitpix != 16 && stats.bitpix != 8) { + return false; + } + QString pattern(bayerPattern); + pattern = pattern.remove("'").trimmed(); + + if (pattern == "RGGB") + debayerParams.filter = DC1394_COLOR_FILTER_RGGB; + else if (pattern == "GBRG") + debayerParams.filter = DC1394_COLOR_FILTER_GBRG; + else if (pattern == "GRBG") + debayerParams.filter = DC1394_COLOR_FILTER_GRBG; + else if (pattern == "BGGR") + debayerParams.filter = DC1394_COLOR_FILTER_BGGR; + // We return unless we find a valid pattern + else + { + return false; + } + + fits_read_key(fptr, TINT, "XBAYROFF", &debayerParams.offsetX, nullptr, &status); + fits_read_key(fptr, TINT, "YBAYROFF", &debayerParams.offsetY, nullptr, &status); + + return true; +} + +bool FITSData::debayer() +{ + if (bayerBuffer == nullptr) + { + int anynull = 0, status = 0; + + bayerBuffer = imageBuffer; + + if (fits_read_img(fptr, data_type, 1, stats.samples_per_channel, 0, bayerBuffer, &anynull, &status)) + { + char errmsg[512]; + fits_get_errstatus(status, errmsg); + return false; + } + } + + switch (data_type) + { + case TBYTE: + return debayer_8bit(); + + case TUSHORT: + return debayer_16bit(); + + default: + return false; + } + + return false; +} + +bool FITSData::debayer_8bit() +{ + dc1394error_t error_code; + + int rgb_size = stats.samples_per_channel * 3 * stats.bytesPerPixel; + uint8_t *destinationBuffer = new uint8_t[rgb_size]; + + int ds1394_height = stats.height; + uint8_t *dc1394_source = bayerBuffer; + + if (debayerParams.offsetY == 1) + { + dc1394_source += stats.width; + ds1394_height--; + } + + if (debayerParams.offsetX == 1) + { + dc1394_source++; + } + + error_code = dc1394_bayer_decoding_8bit(dc1394_source, destinationBuffer, stats.width, ds1394_height, + debayerParams.filter, debayerParams.method); + + if (error_code != DC1394_SUCCESS) + { + channels = 1; + delete[] destinationBuffer; + return false; + } + + if (channels == 1) + { + delete[] imageBuffer; + imageBuffer = new uint8_t[rgb_size]; + } + + // Data in R1G1B1, we need to copy them into 3 layers for FITS + + uint8_t *rBuff = imageBuffer; + uint8_t *gBuff = imageBuffer + (stats.width * stats.height); + uint8_t *bBuff = imageBuffer + (stats.width * stats.height * 2); + + int imax = stats.samples_per_channel * 3 - 3; + for (int i = 0; i <= imax; i += 3) + { + *rBuff++ = destinationBuffer[i]; + *gBuff++ = destinationBuffer[i + 1]; + *bBuff++ = destinationBuffer[i + 2]; + } + + channels = 3; + delete[] destinationBuffer; + bayerBuffer = nullptr; + return true; +} + +bool FITSData::debayer_16bit() +{ + dc1394error_t error_code; + + int rgb_size = stats.samples_per_channel * 3 * stats.bytesPerPixel; + uint8_t *destinationBuffer = new uint8_t[rgb_size]; + + uint16_t *buffer = reinterpret_cast(bayerBuffer); + uint16_t *dstBuffer = reinterpret_cast(destinationBuffer); + + int ds1394_height = stats.height; + uint16_t *dc1394_source = buffer; + + if (debayerParams.offsetY == 1) + { + dc1394_source += stats.width; + ds1394_height--; + } + + if (debayerParams.offsetX == 1) + { + dc1394_source++; + } + + error_code = dc1394_bayer_decoding_16bit(dc1394_source, dstBuffer, stats.width, ds1394_height, debayerParams.filter, + debayerParams.method, 16); + + if (error_code != DC1394_SUCCESS) + { + channels = 1; + delete[] destinationBuffer; + return false; + } + + if (channels == 1) + { + delete[] imageBuffer; + imageBuffer = new uint8_t[rgb_size]; + + if (imageBuffer == nullptr) + { + delete[] destinationBuffer; + return false; + } + } + + buffer = reinterpret_cast(imageBuffer); + + // Data in R1G1B1, we need to copy them into 3 layers for FITS + + uint16_t *rBuff = buffer; + uint16_t *gBuff = buffer + (stats.width * stats.height); + uint16_t *bBuff = buffer + (stats.width * stats.height * 2); + + int imax = stats.samples_per_channel * 3 - 3; + for (int i = 0; i <= imax; i += 3) + { + *rBuff++ = dstBuffer[i]; + *gBuff++ = dstBuffer[i + 1]; + *bBuff++ = dstBuffer[i + 2]; + } + + channels = 3; + delete[] destinationBuffer; + bayerBuffer = nullptr; + return true; +} + +QString FITSData::getLastError() const +{ + return lastError; +} + +QImage FITSData::FITSToImage(const QString &filename) +{ + QImage fitsImage; + double min, max, val; + + FITSData data; + + bool rc = data.loadFITS(filename); + if (rc == false) + return fitsImage; + + data.getMinMax(&min, &max); + + if (min == max) + { + fitsImage.fill(Qt::white); + return fitsImage; + } + + if (data.getNumOfChannels() == 1) + { + fitsImage = QImage(data.getWidth(), data.getHeight(), QImage::Format_Indexed8); + + fitsImage.setColorCount(256); + for (int i = 0; i < 256; i++) + fitsImage.setColor(i, qRgb(i, i, i)); + } + else + { + fitsImage = QImage(data.getWidth(), data.getHeight(), QImage::Format_RGB32); + } + + uint16_t w = data.getWidth(); + uint16_t h = data.getHeight(); + uint32_t size = data.getSize(); + + double dataMin = data.stats.mean[0] - data.stats.stddev[0]; + double dataMax = data.stats.mean[0] + data.stats.stddev[0] * 3; + + double bscale = 255. / (dataMax - dataMin); + double bzero = (-dataMin) * (255. / (dataMax - dataMin)); + + // Long way to do this since we do not want to use templated functions here + switch (data.getDataType()) + { + case TBYTE: + { + uint8_t *buffer = data.getImageBuffer(); + uint8_t bMin = dataMin < 0 ? 0 : dataMin; + uint8_t bMax = dataMax > 255 ? 255 : dataMax; + if (data.getNumOfChannels() == 1) + { + /* Fill in pixel values using indexed map, linear scale */ + for (int j = 0; j < h; j++) + { + unsigned char *scanLine = fitsImage.scanLine(j); + + for (int i = 0; i < w; i++) + { + val = qBound(bMin, buffer[j * w + i], bMax); + val = val * bscale + bzero; + scanLine[i] = qBound(0.0, val, 255.0); + } + } + } + else + { + double rval = 0, gval = 0, bval = 0; + QRgb value; + /* Fill in pixel values using indexed map, linear scale */ + for (int j = 0; j < h; j++) + { + QRgb *scanLine = reinterpret_cast((fitsImage.scanLine(j))); + + for (int i = 0; i < w; i++) + { + rval = qBound(bMin, buffer[j * w + i], bMax); + gval = qBound(bMin, buffer[j * w + i + size], bMax); + bval = qBound(bMin, buffer[j * w + i + size * 2], bMax); + ; + + value = qRgb(rval * bscale + bzero, gval * bscale + bzero, bval * bscale + bzero); + + scanLine[i] = value; + } + } + } + } + break; + + case TUSHORT: + { + uint16_t *buffer = reinterpret_cast(data.getImageBuffer()); + uint16_t bMin = dataMin < 0 ? 0 : dataMin; + uint16_t bMax = dataMax > USHRT_MAX ? USHRT_MAX : dataMax; + if (data.getNumOfChannels() == 1) + { + /* Fill in pixel values using indexed map, linear scale */ + for (int j = 0; j < h; j++) + { + unsigned char *scanLine = fitsImage.scanLine(j); + + for (int i = 0; i < w; i++) + { + val = qBound(bMin, buffer[j * w + i], bMax); + val = val * bscale + bzero; + scanLine[i] = qBound(0.0, val, 255.0); + } + } + } + else + { + double rval = 0, gval = 0, bval = 0; + QRgb value; + /* Fill in pixel values using indexed map, linear scale */ + for (int j = 0; j < h; j++) + { + QRgb *scanLine = reinterpret_cast((fitsImage.scanLine(j))); + + for (int i = 0; i < w; i++) + { + rval = qBound(bMin, buffer[j * w + i], bMax); + gval = qBound(bMin, buffer[j * w + i + size], bMax); + bval = qBound(bMin, buffer[j * w + i + size * 2], bMax); + + value = qRgb(rval * bscale + bzero, gval * bscale + bzero, bval * bscale + bzero); + + scanLine[i] = value; + } + } + } + } + break; + + default: + break; + } + + return fitsImage; +} Index: lib/imagemetainfomodel.cpp =================================================================== --- lib/imagemetainfomodel.cpp +++ lib/imagemetainfomodel.cpp @@ -21,6 +21,11 @@ // Self #include "imagemetainfomodel.h" +#include "config-gwenview.h" +#if HAVE_FITS +#include "fitsformat/fitsdata.h" +#endif + // Qt #include #include @@ -46,6 +51,9 @@ NoGroup = -1, GeneralGroup, ExifGroup, +#if HAVE_FITS + FitsGroup, +#endif IptcGroup, XmpGroup }; @@ -302,9 +310,16 @@ : d(new ImageMetaInfoModelPrivate) { d->q = this; +#if HAVE_FITS + d->mMetaInfoGroupVector.resize(5); +#else d->mMetaInfoGroupVector.resize(4); +#endif d->mMetaInfoGroupVector[GeneralGroup] = new MetaInfoGroup(i18nc("@title:group General info about the image", "General")); d->mMetaInfoGroupVector[ExifGroup] = new MetaInfoGroup("EXIF"); +#if HAVE_FITS + d->mMetaInfoGroupVector[FitsGroup] = new MetaInfoGroup("FITS"); +#endif d->mMetaInfoGroupVector[IptcGroup] = new MetaInfoGroup("IPTC"); d->mMetaInfoGroupVector[XmpGroup] = new MetaInfoGroup("XMP"); d->initGeneralGroup(); @@ -325,6 +340,56 @@ d->setGroupEntryValue(GeneralGroup, "General.Name", item.name()); d->setGroupEntryValue(GeneralGroup, "General.Size", sizeString); d->setGroupEntryValue(GeneralGroup, "General.Time", timeString); + +#if HAVE_FITS + if (url.fileName().endsWith(".fit", Qt::CaseInsensitive) || + url.fileName().endsWith(".fits", Qt::CaseInsensitive)) { + FITSData fitsLoader; + MetaInfoGroup* group = d->mMetaInfoGroupVector[FitsGroup]; + + if (fitsLoader.loadFITS(url.path())) { + QString recordList; + int nkeys = 0; + + fitsLoader.getFITSRecord(recordList, nkeys); + for (int i = 0; i < nkeys; i++) + { + QString record = recordList.mid(i * 80, 80); + QString key; + QString keyStr; + QString value; + + if (!record.contains("=")) { + key = record.section(' ', 0, 0).simplified(); + keyStr = key; + value = record.section(' ', 1, -1).simplified(); + } else { + key = record.section('=', 0, 0).simplified(); + if (record.contains('/')) { + keyStr = record.section('/', -1, -1).simplified(); + value = record.section('=', 1, -1).section('/', 0, 0); + } else { + keyStr = key; + value = record.section('=', 1, -1); + } + value.remove("\'"); + value = value.simplified(); + } + if (value.isEmpty()) + continue; + + // Check if the value is a number and make it more readable + bool ok = false; + float number = value.toFloat(&ok); + + if (ok) + value = QString("%1").arg(number); + + group->addEntry("Fits."+key, keyStr, value); + } + } + } +#endif } void ImageMetaInfoModel::setImageSize(const QSize& size) @@ -390,6 +455,10 @@ group = d->mMetaInfoGroupVector[GeneralGroup]; } else if (key.startsWith(QLatin1String("Exif"))) { group = d->mMetaInfoGroupVector[ExifGroup]; +#if HAVE_FITS + } else if (key.startsWith(QLatin1String("Fits"))) { + group = d->mMetaInfoGroupVector[FitsGroup]; +#endif } else if (key.startsWith(QLatin1String("Iptc"))) { group = d->mMetaInfoGroupVector[IptcGroup]; } else if (key.startsWith(QLatin1String("Xmp"))) { Index: lib/mimetypeutils.cpp =================================================================== --- lib/mimetypeutils.cpp +++ lib/mimetypeutils.cpp @@ -21,6 +21,8 @@ #include "mimetypeutils.h" #include "mimetypeutils_p.h" +#include "config-gwenview.h" + // Qt #include #include @@ -88,6 +90,9 @@ for (auto mime: supported) { list << resolveAlias(QString::fromUtf8(mime)); } +#if HAVE_FITS + list += "image/fits"; +#endif // We don't want svg images to be considered as raster images Q_FOREACH(const QString& mimeType, svgImageMimeTypes()) { list.removeOne(mimeType);