RAW picture file support

This commit is contained in:
simon987 2020-06-21 10:46:11 -04:00
parent 9e4b51f862
commit 266a9fe5d5
7 changed files with 320 additions and 6 deletions

View File

@ -20,7 +20,7 @@ add_library(
libscan/font/font.c libscan/font/font.h
third-party/utf8.h
libscan/mobi/scan_mobi.c libscan/mobi/scan_mobi.h)
libscan/mobi/scan_mobi.c libscan/mobi/scan_mobi.h libscan/raw/raw.c libscan/raw/raw.h)
set_target_properties(scan PROPERTIES LINKER_LANGUAGE C)
set(CMAKE_FIND_LIBRARY_SUFFIXES .a .lib)
@ -45,6 +45,9 @@ find_library(HARFBUZZ_LIB NAMES harfbuzz harfbuzzd)
find_library(FREETYPE_LIB NAMES freetype freetyped)
find_library(LZO2_LIB NAMES lzo2)
find_library(CMS_LIB NAMES lcms)
find_library(JAS_LIB NAMES jasper)
target_compile_options(
scan
@ -146,6 +149,11 @@ target_link_libraries(
z
${CMAKE_THREAD_LIBS_INIT}
raw
-fopenmp
${CMS_LIB}
${JAS_LIB}
)
target_include_directories(

View File

@ -21,13 +21,19 @@
#define ABS(a) (((a) < 0) ? -(a) : (a))
#define APPEND_STR_META(doc, keyname, value) \
meta_line_t *meta_str = malloc(sizeof(meta_line_t) + strlen(value)); \
{meta_line_t *meta_str = malloc(sizeof(meta_line_t) + strlen(value)); \
meta_str->key = keyname; \
strcpy(meta_str->str_val, value); \
APPEND_META(doc, meta_str)
APPEND_META(doc, meta_str)}
#define APPEND_INT_META(doc, keyname, value) \
{meta_line_t *meta_int = malloc(sizeof(meta_line_t)); \
meta_int->key = keyname; \
meta_int->int_val = value; \
APPEND_META(doc, meta_int)}
#define APPEND_TN_META(doc, width, height) \
meta_line_t *meta_str = malloc(sizeof(meta_line_t) + 4 + 1 + 4); \
{meta_line_t *meta_str = malloc(sizeof(meta_line_t) + 4 + 1 + 4); \
meta_str->key = MetaThumbnail; \
sprintf(meta_str->str_val, "%04d,%04d", width, height); \
APPEND_META(doc, meta_str)
APPEND_META(doc, meta_str)}

View File

@ -65,7 +65,7 @@ AVFrame *scale_frame(const AVCodecContext *decoder, const AVFrame *frame, int si
struct SwsContext *sws_ctx = sws_getContext(
decoder->width, decoder->height, decoder->pix_fmt,
dstW, dstH, AV_PIX_FMT_YUVJ420P,
SWS_FAST_BILINEAR, 0, 0, 0
SIST_SWS_ALGO, 0, 0, 0
);
int dst_buf_len = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, dstW, dstH, 1);

186
libscan/raw/raw.c Normal file
View File

@ -0,0 +1,186 @@
#include "raw.h"
#include <libraw/libraw.h>
#include <errno.h>
#include <unistd.h>
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavcodec/avcodec.h"
#include "libavutil/imgutils.h"
#include <fcntl.h>
__always_inline
static AVCodecContext *alloc_jpeg_encoder(scan_raw_ctx_t *ctx, int dstW, int dstH, float qscale) {
AVCodec *jpeg_codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
AVCodecContext *jpeg = avcodec_alloc_context3(jpeg_codec);
jpeg->width = dstW;
jpeg->height = dstH;
jpeg->time_base.den = 1000000;
jpeg->time_base.num = 1;
jpeg->i_quant_factor = qscale;
jpeg->pix_fmt = AV_PIX_FMT_YUVJ420P;
int ret = avcodec_open2(jpeg, jpeg_codec, NULL);
if (ret != 0) {
CTX_LOG_WARNINGF("raw.c", "Could not open jpeg encoder: %s!\n", av_err2str(ret))
return NULL;
}
return jpeg;
}
#define MIN_SIZE 32
void parse_raw(scan_raw_ctx_t *ctx, vfile_t *f, document_t *doc) {
libraw_data_t *libraw_lib = libraw_init(0);
if (!libraw_lib) {
CTX_LOG_ERROR("raw.c", "Cannot create libraw handle")
return;
}
size_t buf_len = 0;
void *buf = read_all(f, &buf_len);
int ret = libraw_open_buffer(libraw_lib, buf, buf_len);
if (ret != 0) {
CTX_LOG_ERROR(f->filepath, "Could not open raw file")
free(buf);
return;
}
ret = libraw_unpack(libraw_lib);
if (ret != 0) {
CTX_LOG_ERROR(f->filepath, "Could not unpack raw file")
free(buf);
libraw_close(libraw_lib);
return;
}
libraw_dcraw_process(libraw_lib);
if (*libraw_lib->idata.model != '\0') {
APPEND_STR_META(doc, MetaExifModel, libraw_lib->idata.model)
}
if (*libraw_lib->idata.make != '\0') {
APPEND_STR_META(doc, MetaExifMake, libraw_lib->idata.make)
}
if (*libraw_lib->idata.software != '\0') {
APPEND_STR_META(doc, MetaExifSoftware, libraw_lib->idata.software)
}
APPEND_INT_META(doc, MetaWidth, libraw_lib->sizes.width)
APPEND_INT_META(doc, MetaHeight, libraw_lib->sizes.height)
char tmp[1024];
snprintf(tmp, sizeof(tmp), "%g", libraw_lib->other.iso_speed);
APPEND_STR_META(doc, MetaExifIsoSpeedRatings, tmp)
if (*libraw_lib->other.desc != '\0') {
APPEND_STR_META(doc, MetaContent, libraw_lib->other.desc)
}
if (*libraw_lib->other.artist != '\0') {
APPEND_STR_META(doc, MetaArtist, libraw_lib->other.artist)
}
struct tm *time = localtime(&libraw_lib->other.timestamp);
strftime(tmp, sizeof(tmp), "%Y:%m:%d %H:%M:%S", time);
APPEND_STR_META(doc, MetaExifDateTime, tmp)
snprintf(tmp, sizeof(tmp), "%.1f", libraw_lib->other.focal_len);
APPEND_STR_META(doc, MetaExifFocalLength, tmp)
snprintf(tmp, sizeof(tmp), "%.1f", libraw_lib->other.aperture);
APPEND_STR_META(doc, MetaExifFNumber, tmp)
APPEND_STR_META(doc, MetaMediaVideoCodec, "raw")
if (ctx->tn_size <= 0) {
free(buf);
libraw_close(libraw_lib);
return;
}
int errc = 0;
libraw_processed_image_t *img = libraw_dcraw_make_mem_image(libraw_lib, &errc);
if (errc != 0) {
free(buf);
libraw_dcraw_clear_mem(img);
libraw_close(libraw_lib);
return;
}
int dstW;
int dstH;
if (img->width <= ctx->tn_size && img->height <= ctx->tn_size) {
dstW = img->width;
dstH = img->height;
} else {
double ratio = (double) img->width / img->height;
if (img->width > img->height) {
dstW = ctx->tn_size;
dstH = (int) (ctx->tn_size / ratio);
} else {
dstW = (int) (ctx->tn_size * ratio);
dstH = ctx->tn_size;
}
}
if (dstW <= MIN_SIZE || dstH <= MIN_SIZE) {
free(buf);
libraw_dcraw_clear_mem(img);
libraw_close(libraw_lib);
return;
}
AVFrame *scaled_frame = av_frame_alloc();
struct SwsContext *sws_ctx= sws_getContext(
img->width, img->height, AV_PIX_FMT_RGB24,
dstW, dstH, AV_PIX_FMT_YUVJ420P,
SIST_SWS_ALGO, 0, 0, 0
);
int dst_buf_len = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, dstW, dstH, 1);
uint8_t *dst_buf = (uint8_t *) av_malloc(dst_buf_len);
av_image_fill_arrays(scaled_frame->data, scaled_frame->linesize, dst_buf, AV_PIX_FMT_YUV420P, dstW, dstH, 1);
const uint8_t *inData[1] = {img->data};
int inLinesize[1] = {3 * img->width};
sws_scale(sws_ctx,
inData, inLinesize,
0, img->height,
scaled_frame->data, scaled_frame->linesize
);
scaled_frame->width = dstW;
scaled_frame->height = dstH;
scaled_frame->format = AV_PIX_FMT_YUV420P;
sws_freeContext(sws_ctx);
AVCodecContext *jpeg_encoder = alloc_jpeg_encoder(ctx, scaled_frame->width, scaled_frame->height, 1.0f);
avcodec_send_frame(jpeg_encoder, scaled_frame);
AVPacket jpeg_packet;
av_init_packet(&jpeg_packet);
avcodec_receive_packet(jpeg_encoder, &jpeg_packet);
APPEND_TN_META(doc, scaled_frame->width, scaled_frame->height)
ctx->store((char *) doc->uuid, sizeof(doc->uuid), (char *) jpeg_packet.data, jpeg_packet.size);
av_packet_unref(&jpeg_packet);
av_free(*scaled_frame->data);
av_frame_free(&scaled_frame);
avcodec_free_context(&jpeg_encoder);
libraw_dcraw_clear_mem(img);
libraw_close(libraw_lib);
free(buf);
}

17
libscan/raw/raw.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef SIST2_RAW_H
#define SIST2_RAW_H
#include "../scan.h"
typedef struct {
int tn_size;
float tn_qscale;
log_callback_t log;
logf_callback_t logf;
store_callback_t store;
} scan_raw_ctx_t;
void parse_raw(scan_raw_ctx_t *ctx, vfile_t *f, document_t *doc);
#endif //SIST2_RAW_H

View File

@ -7,6 +7,8 @@
#include "macros.h"
#define SIST_SWS_ALGO SWS_LANCZOS
#define META_INT_MASK 0x80
#define META_STR_MASK 0x40
#define META_LONG_MASK 0x20

View File

@ -8,6 +8,7 @@ extern "C" {
#include "../libscan/media/media.h"
#include "../libscan/ooxml/ooxml.h"
#include "../libscan/mobi/scan_mobi.h"
#include "../libscan/raw/raw.h"
#include <libavutil/avutil.h>
}
@ -25,6 +26,8 @@ static scan_ooxml_ctx_t ooxml_500_ctx;
static scan_mobi_ctx_t mobi_500_ctx;
static scan_raw_ctx_t raw_ctx;
document_t LastSubDoc;
@ -455,6 +458,92 @@ TEST(Arc, Utf8) {
cleanup(&doc, &f);
}
/* RAW */
TEST(RAW, Panasonic) {
vfile_t f;
document_t doc;
load_doc_file("libscan-test-files/test_files/raw/Panasonic.RW2", &f, &doc);
parse_raw(&raw_ctx, &f, &doc);
ASSERT_STREQ(get_meta(&doc, MetaMediaVideoCodec)->str_val, "raw");
ASSERT_STREQ(get_meta(&doc, MetaExifModel)->str_val, "DMC-GX8");
ASSERT_STREQ(get_meta(&doc, MetaExifMake)->str_val, "Panasonic");
ASSERT_STREQ(get_meta(&doc, MetaExifIsoSpeedRatings)->str_val, "640");
ASSERT_STREQ(get_meta(&doc, MetaExifDateTime)->str_val, "2020:07:20 10:00:34");
ASSERT_STREQ(get_meta(&doc, MetaExifFocalLength)->str_val, "20.0");
ASSERT_STREQ(get_meta(&doc, MetaExifFNumber)->str_val, "2.0");
ASSERT_EQ(get_meta(&doc, MetaWidth)->int_val, 5200);
ASSERT_EQ(get_meta(&doc, MetaHeight)->int_val, 3904);
cleanup(&doc, &f);
}
TEST(RAW, Nikon) {
vfile_t f;
document_t doc;
load_doc_file("libscan-test-files/test_files/raw/Nikon.NEF", &f, &doc);
parse_raw(&raw_ctx, &f, &doc);
ASSERT_STREQ(get_meta(&doc, MetaMediaVideoCodec)->str_val, "raw");
ASSERT_STREQ(get_meta(&doc, MetaExifModel)->str_val, "D750");
ASSERT_STREQ(get_meta(&doc, MetaExifMake)->str_val, "Nikon");
ASSERT_EQ(get_meta(&doc, MetaWidth)->int_val, 6032);
ASSERT_EQ(get_meta(&doc, MetaHeight)->int_val, 4032);
cleanup(&doc, &f);
}
TEST(RAW, Sony) {
vfile_t f;
document_t doc;
load_doc_file("libscan-test-files/test_files/raw/Sony.ARW", &f, &doc);
parse_raw(&raw_ctx, &f, &doc);
ASSERT_STREQ(get_meta(&doc, MetaMediaVideoCodec)->str_val, "raw");
ASSERT_STREQ(get_meta(&doc, MetaExifModel)->str_val, "ILCE-7RM3");
ASSERT_STREQ(get_meta(&doc, MetaExifMake)->str_val, "Sony");
ASSERT_EQ(get_meta(&doc, MetaWidth)->int_val, 7968);
ASSERT_EQ(get_meta(&doc, MetaHeight)->int_val, 5320);
cleanup(&doc, &f);
}
TEST(RAW, Olympus) {
vfile_t f;
document_t doc;
load_doc_file("libscan-test-files/test_files/raw/Olympus.ORF", &f, &doc);
parse_raw(&raw_ctx, &f, &doc);
ASSERT_STREQ(get_meta(&doc, MetaMediaVideoCodec)->str_val, "raw");
ASSERT_STREQ(get_meta(&doc, MetaExifModel)->str_val, "E-M5MarkII");
ASSERT_STREQ(get_meta(&doc, MetaExifMake)->str_val, "Olympus");
ASSERT_EQ(get_meta(&doc, MetaWidth)->int_val, 4640);
ASSERT_EQ(get_meta(&doc, MetaHeight)->int_val, 3472);
cleanup(&doc, &f);
}
TEST(RAW, Fuji) {
vfile_t f;
document_t doc;
load_doc_file("libscan-test-files/test_files/raw/Fuji.RAF", &f, &doc);
parse_raw(&raw_ctx, &f, &doc);
ASSERT_STREQ(get_meta(&doc, MetaMediaVideoCodec)->str_val, "raw");
ASSERT_STREQ(get_meta(&doc, MetaExifModel)->str_val, "X-T2");
ASSERT_STREQ(get_meta(&doc, MetaExifMake)->str_val, "Fujifilm");
ASSERT_EQ(get_meta(&doc, MetaWidth)->int_val, 6032);
ASSERT_EQ(get_meta(&doc, MetaHeight)->int_val, 4028);
cleanup(&doc, &f);
}
int main(int argc, char **argv) {
setlocale(LC_ALL, "");
@ -500,6 +589,12 @@ int main(int argc, char **argv) {
mobi_500_ctx.log = noop_log;
mobi_500_ctx.logf = noop_logf;
raw_ctx.log = noop_log;
raw_ctx.logf = noop_logf;
raw_ctx.store = noop_store;
raw_ctx.tn_size = 500;
raw_ctx.tn_qscale = 5.0;
av_log_set_level(AV_LOG_QUIET);
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();