Blob Blame History Raw
/*
 * libopenraw - ordiag.cpp
 *
 * Copyright (C) 2007-2016 Hubert Figuiere
 * Copyright (C) 2008 Novell, Inc.
 *
 * 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 3 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, see
 * <http://www.gnu.org/licenses/>.
 */


#include <unistd.h>

#include <algorithm>
#include <iostream>
#include <memory>
#include <set>
#include <string>
#include <vector>

#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>

#include <libopenraw/libopenraw.h>

/**
 * Dump on RawFile. (functor)
 */
class OrDiag
{
public:
    /** constructor
     * @param out the output stream
     */
    OrDiag(std::ostream & out, const std::string & extract_thumbs)
        : m_out(out)
        , m_extract_all_thumbs(false)
        {
            m_extract_all_thumbs = (extract_thumbs == "all");
            if (!m_extract_all_thumbs) {
                try {
                    int size = boost::lexical_cast<int>(extract_thumbs);
                    m_thumb_sizes.insert(size);
                }
                catch(...) {

                }
            }
        }

    std::string cfaPatternToString(ORCfaPatternRef pattern)
        {
            if(pattern == NULL) {
                return "(null)";
            }

            std::string out;
            uint16_t size = 0;
            const uint8_t* patternPattern
              = or_cfapattern_get_pattern(pattern, &size);

            for(uint16_t i = 0; i < size; ++i) {
                switch(patternPattern[i]) {
                case OR_PATTERN_COLOUR_RED:
                    out.push_back('R');
                    break;
                case OR_PATTERN_COLOUR_GREEN:
                    out.push_back('G');
                    break;

                case OR_PATTERN_COLOUR_BLUE:
                    out.push_back('B');
                    break;

                default:
                    out.push_back('*');
                    break;
                }
            }

            return out;
        }
    std::string cfaPatternToString(::or_cfa_pattern t)
        {
            switch(t) {
            case OR_CFA_PATTERN_NONE:
                return "None";
                break;
            case OR_CFA_PATTERN_NON_RGB22:
                return "Non RGB 2x2";
                break;
            case OR_CFA_PATTERN_RGGB:
                return "R,G,G,B";
                break;
            case OR_CFA_PATTERN_GBRG:
                return "G,B,R,G";
                break;
            case OR_CFA_PATTERN_BGGR:
                return "B,G,G,R";
                break;
            case OR_CFA_PATTERN_GRBG:
                return "G,R,B,G";
                break;
            default:
                break;
            }
            return str(boost::format("Unknown %1%") % t);
        };

    std::string dataTypeToString(or_data_type t)
        {
            switch(t) {
            case OR_DATA_TYPE_NONE:
                return "None";
                break;
            case OR_DATA_TYPE_PIXMAP_8RGB:
                return "8bits per channel RGB pixmap";
                break;
            case OR_DATA_TYPE_JPEG:
                return "JPEG data";
                break;
            case OR_DATA_TYPE_TIFF:
                return "TIFF container";
                break;
            case OR_DATA_TYPE_PNG:
                return "PNG container";
                break;
            case OR_DATA_TYPE_RAW:
                return "RAW data";
                break;
            case OR_DATA_TYPE_COMPRESSED_RAW:
                return "Compressed RAW data";
                break;
            case OR_DATA_TYPE_UNKNOWN:
                return "Unknown type";
                break;
            default:
                break;
            }
            return "Invalid";
        }

    /** return a string for the raw file type
     */
    std::string typeToString(or_rawfile_type t)
        {
            switch(t) {
            case OR_RAWFILE_TYPE_UNKNOWN:
                break;
            case OR_RAWFILE_TYPE_CR2:
                return "Canon CR2";
                break;
            case OR_RAWFILE_TYPE_CRW:
                return "Canon CRW";
                break;
            case OR_RAWFILE_TYPE_NEF:
                return "Nikon NEF";
                break;
            case OR_RAWFILE_TYPE_MRW:
                return "Minolta MRW";
                break;
            case OR_RAWFILE_TYPE_ARW:
                return "Sony ARW";
                break;
            case OR_RAWFILE_TYPE_DNG:
                return "Adobe DNG";
                break;
            case OR_RAWFILE_TYPE_ORF:
                return "Olympus ORF";
                break;
            case OR_RAWFILE_TYPE_PEF:
                return "Pentax PEF";
                break;
            case OR_RAWFILE_TYPE_ERF:
                return "Epson ERF";
                break;
            case OR_RAWFILE_TYPE_RW2:
                return "Panasonic RAW";
                break;
            case OR_RAWFILE_TYPE_RAF:
                return "FujiFilm RAF";
                break;
            default:
                break;
            }
            return "Unknown";
        }

    /** Extract thumbnail to a file
     */
    std::string extractThumb(ORThumbnailRef thumb)
        {
            FILE* f;
            size_t s;
            std::string ext;
            switch(or_thumbnail_format(thumb)) {
            case OR_DATA_TYPE_PIXMAP_8RGB:
                ext = "ppm";
                break;
            case OR_DATA_TYPE_JPEG:
                ext = "jpg";
                break;
            default:
                break;
            }
            if (ext.empty()) {
                return "";
            }

            uint32_t x, y;
            or_thumbnail_dimensions(thumb, &x, &y);
            uint32_t dim = std::max(x, y);
            std::string name(str(boost::format("thumb_%1%.%2%") % dim  % ext));
            f = fopen(name.c_str(), "wb");
            if (or_thumbnail_format(thumb) == OR_DATA_TYPE_PIXMAP_8RGB) {
                // ppm preemble.
                fprintf(f, "P6\n");
                fprintf(f, "%d %d\n", x, y);
                fprintf(f, "%d\n", /*(componentsize == 2) ? 0xffff :*/ 0xff);
            }
            size_t dataSize = or_thumbnail_data_size(thumb);
            s = fwrite(or_thumbnail_data(thumb), 1, dataSize, f);
            if(s != dataSize) {
                std::cerr << "short write of " << s << " bytes\n";
            }
            fclose(f);

            return name;
        }

    /** dump the previews of the raw file to mout
     */
    void dumpPreviews(ORRawFileRef rf)
        {
            size_t size = 0;
            const uint32_t * previews = or_rawfile_get_thumbnail_sizes(rf, &size);
            m_out << boost::format("\tNumber of previews: %1%\n")
                % size;

            m_out << "\tAvailable previews:\n";
            for(size_t i = 0; i < size; i++) {

                m_out << boost::format("\t\tSize %1%\n") % previews[i];

                ORThumbnailRef thumb = or_thumbnail_new();
                ::or_error err = or_rawfile_get_thumbnail(rf, previews[i], thumb);
                if (err != OR_ERROR_NONE) {
                    m_out << boost::format("\t\t\tError getting thumbnail %1%\n") % err;
                }
                else {
                    m_out << boost::format("\t\t\tFormat %1%\n")
                        % dataTypeToString(or_thumbnail_format(thumb));
                    uint32_t x, y;
                    or_thumbnail_dimensions(thumb, &x, &y);
                    m_out << boost::format("\t\t\tDimensions: width = %1% height = %2%\n")
                        % x % y;
                    m_out << boost::format("\t\t\tByte size: %1%\n")
                        % or_thumbnail_data_size(thumb);
                }
                if (m_extract_all_thumbs
                    || m_thumb_sizes.find(previews[i]) != m_thumb_sizes.end()) {

                    std::string name = extractThumb(thumb);

                    m_out << boost::format("\t\t\tOutput as %1%\n") % name;
                }
                or_thumbnail_release(thumb);
            }
        }

    void dumpRawData(ORRawFileRef rf)
        {
            ORRawDataRef rd = or_rawdata_new();
            ::or_error err = or_rawfile_get_rawdata(rf, rd, 0);
            if (err == OR_ERROR_NONE) {
                m_out << "\tRAW data\n";
                or_data_type dataType = or_rawdata_format(rd);
                m_out << boost::format("\t\tType: %1%")
                    % dataTypeToString(dataType);
                if(dataType == OR_DATA_TYPE_COMPRESSED_RAW)  {
                    m_out << boost::format(" (compression = %1%)\n")
                        % or_rawdata_get_compression(rd);
                }
                else {
                    m_out << "\n";
                }
                m_out << boost::format("\t\tByte size: %1%\n")
                    % or_rawdata_data_size(rd);
                uint32_t x, y;
                or_rawdata_dimensions(rd, &x, &y);
                m_out << boost::format("\t\tDimensions: width = %1% height = %2%\n")
                    % x % y;
                uint32_t roi_x, roi_y, roi_width, roi_height;
                or_rawdata_get_roi(rd, &roi_x, &roi_y, &roi_width, &roi_height);
                m_out << boost::format("\t\tROI: %1% %2% %3% %4%\n")
                    % roi_x % roi_y % roi_width % roi_height;
                ORCfaPatternRef pattern = or_rawdata_get_cfa_pattern(rd);
                ::or_cfa_pattern patternType
                      = pattern ? or_cfapattern_get_type(pattern)
                      : OR_CFA_PATTERN_NON_RGB22;
                m_out << boost::format("\t\tBayer Type: %1%\n")
                    % cfaPatternToString(patternType);

                if(patternType == OR_CFA_PATTERN_NON_RGB22) {
                    m_out << boost::format("\t\tPattern: %1%\n")
                        % cfaPatternToString(pattern);
                }

                m_out << boost::format("\t\tBits per channel: %1%\n")
                    % or_rawdata_bpc(rd);
                uint16_t black, white;
                or_rawdata_get_levels(rd, &black, &white);
                m_out << boost::format(
                    "\t\tValues: black = %1% white = %2%\n") % black % white;

                uint32_t matrix_size = 0;
                const double *matrix = or_rawdata_get_colour_matrix(rd, 1, &matrix_size);
                if (matrix) {
                    m_out << boost::format("\t\tColour Matrix 1: ");
                    for (uint32_t i = 0; i < matrix_size; i++) {
                        if (i > 0) {
                            m_out << ", ";
                        }
                        m_out << matrix[i];
                    }
                    m_out << "\n";
                }
            }
            else {
                m_out << boost::format("\tNo Raw Data found! (error = %1%)\n")
                    % err;
            }
            or_rawdata_release(rd);
        }
    void dumpMetaData(ORRawFileRef rf)
        {
            int32_t o = or_rawfile_get_orientation(rf);
            m_out << "\tMeta data\n";
            m_out << boost::format("\t\tOrientation: %1%\n")
                % o;
            double matrix[9];
            uint32_t size = 9;

            ExifLightsourceValue calIll;
            calIll = or_rawfile_get_calibration_illuminant1(rf);
            m_out << boost::format("\t\tCalibration Illuminant 1: %1%\n")
                % static_cast<int>(calIll);

            ::or_error err = or_rawfile_get_colourmatrix1(rf, matrix, &size);
            if(err == OR_ERROR_NONE) {
                m_out << boost::format("\t\tColour Matrix 1: %1%, %2%, %3%, "
                                       "%4%, %5%, %6%, %7%, %8%, %9%\n")
                    % matrix[0] % matrix[1] % matrix[2]
                    % matrix[3] % matrix[4] % matrix[5]
                    % matrix[6] % matrix[7] % matrix[8];
            }
            else {
                m_out << "\t\tNo Colour Matrix 1\n";
            }

            calIll = or_rawfile_get_calibration_illuminant2(rf);
            m_out << boost::format("\t\tCalibration Illuminant 2: %1%\n")
                % static_cast<int>(calIll);

            size = 9;
            err = or_rawfile_get_colourmatrix2(rf, matrix, &size);
            if(err == OR_ERROR_NONE) {
                m_out << boost::format("\t\tColour Matrix 2: %1%, %2%, %3%, "
                                       "%4%, %5%, %6%, %7%, %8%, %9%\n")
                    % matrix[0] % matrix[1] % matrix[2]
                    % matrix[3] % matrix[4] % matrix[5]
                    % matrix[6] % matrix[7] % matrix[8];
            }
            else {
                m_out << "\t\tNo Colour Matrix 2\n";
            }
        }
    void operator()(const std::string &s)
        {
            m_out << boost::format("Dumping %1%\n") % s;

            ORRawFileRef rf = or_rawfile_new(s.c_str(), OR_RAWFILE_TYPE_UNKNOWN);

            //std::unique_ptr<RawFile> rf(RawFile::newRawFile(s.c_str()));

            if (rf == NULL) {
                m_out << "unrecognized file\n";
            }
            else {
                or_rawfile_type fileType = or_rawfile_get_type(rf);
                m_out << boost::format("\tType = %1% (%2%)\n")
                    % fileType % typeToString(fileType);
                or_rawfile_typeid fileTypeId = or_rawfile_get_typeid(rf);
                std::string typeId
                    = str(boost::format("%1%, %2%")
                          % OR_GET_FILE_TYPEID_VENDOR(fileTypeId)
                          % OR_GET_FILE_TYPEID_CAMERA(fileTypeId));
                m_out << boost::format("\tType ID = %1%\n") % typeId;

                ORConstMetaValueRef make
                    = or_rawfile_get_metavalue(rf, META_NS_TIFF | EXIF_TAG_MAKE);
                if (make) {
                    m_out << boost::format("\tMake = %1%\n")
                        % or_metavalue_get_string(make, 0);
                }
                ORConstMetaValueRef model
                    = or_rawfile_get_metavalue(rf, META_NS_TIFF | EXIF_TAG_MODEL);
                if (model) {
                    m_out << boost::format("\tModel = %1%\n")
                        % or_metavalue_get_string(model, 0);
                }
                ORConstMetaValueRef uniqueCameraModel
                    = or_rawfile_get_metavalue(rf, META_NS_TIFF
                                       | DNG_TAG_UNIQUE_CAMERA_MODEL);
                if (uniqueCameraModel) {
                    m_out << boost::format("\tUnique Camera Model = %1%\n")
                        % or_metavalue_get_string(uniqueCameraModel, 0);
                }
                dumpPreviews(rf);
                dumpRawData(rf);
                dumpMetaData(rf);
            }
            or_rawfile_release(rf);
        }
private:
    std::ostream & m_out;
    bool m_extract_all_thumbs;
    std::set<int> m_thumb_sizes;
};


void print_help()
{
    std::cerr << "ordiag [-v] [-h] [-t all|<size>] [-d 0-9] [files...]\n";
    std::cerr << "Print libopenraw diagnostics\n";
    std::cerr << "\t-h: show this help\n";
    std::cerr << "\t-v: show version\n";
    std::cerr << "\t-d level: set debug / verbosity to level\n";
    std::cerr << "\t-t [all|<size>]: extract thumbnails. all or <size>.\n";
    std::cerr << "\tfiles: the files to diagnose\n";
}

void print_version()
{
    std::cerr << "ordiag version 0.1 - (c) 2007-2014 Hubert Figuiere\n";
}



int main(int argc, char **argv)
{
    int done = 0;
    int dbl = 0;
    std::string extract_thumbs;
    std::vector<std::string> files;

    int o;
    while((o = getopt(argc, argv, "hvdt:")) != -1) {
        switch (o) {
        case 'h':
            print_help();
            done = 1;
            break;
        case 'v':
            print_version();
            done = 1;
            break;
        case 'd':
            dbl++;
            break;
        case 't':
            if(optarg) {
                extract_thumbs = optarg;
            }
            else {
                print_help();
                done = 1;
            }
            break;
        case '?':
            break;
        default:
            break;
        }
    }
    if (done) {
        return 1;
    }
    for ( ; optind < argc; optind++) {
        files.push_back(argv[optind]);
    }

    if (files.empty()) {
        std::cerr << "missing file name.\n";
        if (dbl) {
            print_version();
        }
        print_help();
        return 1;
    }

    if (dbl >=2) {
        or_debug_set_level(DEBUG2);
    }
    // do the business.
    for_each(files.begin(), files.end(), OrDiag(std::cout, extract_thumbs));

    return 0;
}
/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0))
  indent-tabs-mode:nil
  fill-column:80
  End:
*/