Files
UltraGrid/gpujpeg/src/main.c
2012-08-15 14:59:37 +02:00

620 lines
26 KiB
C

/**
* Copyright (c) 2011, CESNET z.s.p.o
* Copyright (c) 2011, Silicon Genome, LLC.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <libgpujpeg/gpujpeg.h>
#include <libgpujpeg/gpujpeg_util.h>
#include <getopt.h>
void
print_help()
{
printf(
"gpujpeg [options] input.rgb output.jpg [input2.rgb output2.jpg ...]\n"
" -h, --help print help\n"
" -v, --verbose verbose output\n"
" -D, --device set cuda device id (default 0)\n"
" --device-list list cuda devices\n"
"\n"
" -s, --size set input image size in pixels, e.g. 1920x1080\n"
" -C, --comp-count set input/output image number of components (1 or 3)\n"
" -f, --sampling-factor set input/output image sampling factor, e.g. 4:2:2\n"
" -c, --colorspace set input/output image colorspace, e.g. rgb, yuv,\n"
" ycbcr, ycbcr-jpeg, ycbcr-bt601, ycbcr-bt709\n"
"\n"
" -q, --quality set JPEG encoder quality level 0-100 (default 75)\n"
" -r, --restart set JPEG encoder restart interval (default 8)\n"
" --subsampled set JPEG encoder to use chroma subsampling\n"
" -i --interleaved set JPEG encoder to use interleaved stream\n"
" -g --segment-info set JPEG encoder to use segment info in stream\n"
" for fast decoding\n"
"\n"
" -e, --encode perform JPEG encoding\n"
" -d, --decode perform JPEG decoding\n"
" --convert convert input image to output image (change\n"
" color space and/or sampling factor)\n"
" --component-range show samples range for each component in image\n"
"\n"
" -n --iterate perform encoding/decoding in specified number of\n"
" iterations for each image\n"
" -o --use-opengl use an OpenGL texture as input/output\n"
);
}
int
main(int argc, char *argv[])
{
#define OPTION_DEVICE_INFO 1
#define OPTION_SUBSAMPLED 2
#define OPTION_CONVERT 3
#define OPTION_COMPONENT_RANGE 4
struct option longopts[] = {
{"help", no_argument, 0, 'h'},
{"verbose", no_argument, 0, 'v'},
{"device", required_argument, 0, 'D'},
{"device-list", no_argument, 0, OPTION_DEVICE_INFO },
{"size", required_argument, 0, 's'},
{"comp-count", required_argument, 0, 'C'},
{"sampling-factor", required_argument, 0, 'f'},
{"colorspace", required_argument, 0, 'c'},
{"quality", required_argument, 0, 'q'},
{"restart", required_argument, 0, 'r'},
{"segment-info", optional_argument, 0, 'g' },
{"subsampled", optional_argument, 0, OPTION_SUBSAMPLED },
{"interleaved", optional_argument, 0, 'i'},
{"encode", no_argument, 0, 'e'},
{"decode", no_argument, 0, 'd'},
{"convert", no_argument, 0, OPTION_CONVERT },
{"component-range", no_argument, 0, OPTION_COMPONENT_RANGE },
{"iterate", required_argument, 0, 'n' },
{"use-opengl", no_argument, 0, 'o' },
0
};
// Default coder parameters
struct gpujpeg_parameters param;
gpujpeg_set_default_parameters(&param);
// Default image parameters
struct gpujpeg_image_parameters param_image;
gpujpeg_image_set_default_parameters(&param_image);
// Original image parameters in conversion
struct gpujpeg_image_parameters param_image_original;
gpujpeg_image_set_default_parameters(&param_image_original);
// Other parameters
int device_id = 0;
int encode = 0;
int decode = 0;
int convert = 0;
int component_range = 0;
int iterate = 1;
int use_opengl = 0;
// Flags
int restart_interval_default = 1;
int chroma_subsampled = 0;
// Parse command line
char ch = '\0';
int optindex = 0;
char* pos = 0;
while ( (ch = getopt_long(argc, argv, "hvs:q:r:ed", longopts, &optindex)) != -1 ) {
switch (ch) {
case 'h':
print_help();
return 0;
case 'v':
param.verbose = 1;
break;
case 's':
param_image.width = atoi(optarg);
pos = strstr(optarg, "x");
if ( pos == NULL || param_image.width == 0 || (strlen(pos) >= strlen(optarg)) ) {
print_help();
return -1;
}
param_image.height = atoi(pos + 1);
break;
case 'C':
param_image.comp_count = atoi(optarg);
if ( param_image.comp_count != 1 && param_image.comp_count != 3 ) {
fprintf(stderr, "Component count '%s' is not available!\n", optarg);
param_image.comp_count = 3;
}
break;
case 'c':
if ( strcmp(optarg, "none") == 0 )
param_image.color_space = GPUJPEG_NONE;
else if ( strcmp(optarg, "rgb") == 0 )
param_image.color_space = GPUJPEG_RGB;
else if ( strcmp(optarg, "yuv") == 0 )
param_image.color_space = GPUJPEG_YUV;
else if ( strcmp(optarg, "ycbcr") == 0 )
param_image.color_space = GPUJPEG_YCBCR;
else if ( strcmp(optarg, "ycbcr-jpeg") == 0 )
param_image.color_space = GPUJPEG_YCBCR_BT601_256LVLS;
else if ( strcmp(optarg, "ycbcr-bt601") == 0 )
param_image.color_space = GPUJPEG_YCBCR_BT601;
else if ( strcmp(optarg, "ycbcr-bt709") == 0 )
param_image.color_space = GPUJPEG_YCBCR_BT709;
else
fprintf(stderr, "Colorspace '%s' is not available!\n", optarg);
break;
case 'f':
if ( strcmp(optarg, "4:4:4") == 0 )
param_image.sampling_factor = GPUJPEG_4_4_4;
else if ( strcmp(optarg, "4:2:2") == 0 )
param_image.sampling_factor = GPUJPEG_4_2_2;
else
fprintf(stderr, "Sampling factor '%s' is not available!\n", optarg);
break;
case 'q':
param.quality = atoi(optarg);
if ( param.quality < 0 )
param.quality = 0;
if ( param.quality > 100 )
param.quality = 100;
break;
case 'r':
param.restart_interval = atoi(optarg);
if ( param.restart_interval < 0 )
param.restart_interval = 0;
restart_interval_default = 0;
break;
case 'g':
if ( optarg == NULL || strcmp(optarg, "true") == 0 || atoi(optarg) )
param.segment_info = 1;
else
param.segment_info = 0;
break;
case OPTION_SUBSAMPLED:
gpujpeg_parameters_chroma_subsampling(&param);
chroma_subsampled = 1;
break;
case OPTION_DEVICE_INFO:
gpujpeg_print_devices_info();
return 0;
case 'i':
if ( optarg == NULL || strcmp(optarg, "true") == 0 || atoi(optarg) )
param.interleaved = 1;
else
param.interleaved = 0;
break;
case 'e':
encode = 1;
break;
case 'd':
decode = 1;
break;
case 'D':
device_id = atoi(optarg);
break;
case OPTION_CONVERT:
convert = 1;
memcpy(&param_image_original, &param_image, sizeof(struct gpujpeg_image_parameters));
break;
case OPTION_COMPONENT_RANGE:
component_range = 1;
break;
case 'n':
iterate = atoi(optarg);
break;
case 'o':
use_opengl = 1;
break;
case '?':
return -1;
default:
print_help();
return -1;
}
}
argc -= optind;
argv += optind;
// Show info about image samples range
if ( component_range == 1 ) {
// For each image
for ( int index = 0; index < argc; index++ ) {
gpujpeg_image_range_info(argv[index], param_image.width, param_image.height, param_image.sampling_factor);
}
return 0;
}
// Source image and target image must be presented
if ( argc < 2 ) {
fprintf(stderr, "Please supply source and destination image filename!\n");
print_help();
return -1;
}
// Init device
int flags = GPUJPEG_VERBOSE;
if ( use_opengl ) {
flags |= GPUJPEG_OPENGL_INTEROPERABILITY;
gpujpeg_opengl_init();
}
if ( gpujpeg_init_device(device_id, flags) != 0 )
return -1;
// Convert
if ( convert == 1 ) {
// Encode images
for ( int index = 0; index < argc; index += 2 ) {
// Get input and output image
const char* input = argv[index];
const char* output = argv[index + 1];
// Perform conversion
gpujpeg_image_convert(input, output, param_image_original, param_image);
}
return 0;
}
// Detect action if none is specified
if ( encode == 0 && decode == 0 ) {
enum gpujpeg_image_file_format input_format = gpujpeg_image_get_file_format(argv[0]);
enum gpujpeg_image_file_format output_format = gpujpeg_image_get_file_format(argv[1]);
if ( (input_format & GPUJPEG_IMAGE_FILE_RAW) && output_format == GPUJPEG_IMAGE_FILE_JPEG ) {
encode = 1;
} else if ( input_format == GPUJPEG_IMAGE_FILE_JPEG && (output_format & GPUJPEG_IMAGE_FILE_RAW) ) {
decode = 1;
} else {
fprintf(stderr, "Action can't be recognized for specified images!\n");
fprintf(stderr, "You must specify --encode or --decode option!\n");
return -1;
}
}
// Detect color spalce
if ( gpujpeg_image_get_file_format(argv[0]) == GPUJPEG_IMAGE_FILE_YUV && param_image.color_space == GPUJPEG_RGB ) {
param_image.color_space = GPUJPEG_YUV;
}
// Detect component count
if ( gpujpeg_image_get_file_format(argv[0]) == GPUJPEG_IMAGE_FILE_GRAY && param_image.comp_count != 1 ) {
param_image.comp_count = 1;
}
// Adjust restart interval
if ( restart_interval_default == 1 ) {
// when chroma subsampling and interleaving is enabled, the restart interval should be smaller
if ( chroma_subsampled == 1 && param.interleaved == 1 ) {
param.restart_interval = 2;
}
else {
// Adjust according to Mpix count
double coefficient = ((double)param_image.width * param_image.height * param_image.comp_count) / (1000000.0 * 3.0);
if ( coefficient < 1.0 ) {
param.restart_interval = 4;
} else if ( coefficient < 3.0 ) {
param.restart_interval = 8;
} else if ( coefficient < 9.0 ) {
param.restart_interval = 10;
} else {
param.restart_interval = 12;
}
}
if ( param.verbose ) {
printf("\nAuto-adjusting restart interval to %d for better performance.\n", param.restart_interval);
}
}
if ( encode == 1 ) {
// Create OpenGL texture
struct gpujpeg_opengl_texture* texture = NULL;
if ( use_opengl ) {
assert(param_image.sampling_factor == GPUJPEG_4_4_4);
int texture_id = gpujpeg_opengl_texture_create(param_image.width, param_image.height, NULL);
assert(texture_id != 0);
texture = gpujpeg_opengl_texture_register(texture_id, GPUJPEG_OPENGL_TEXTURE_READ);
assert(texture != NULL);
}
// Create encoder
struct gpujpeg_encoder* encoder = gpujpeg_encoder_create(&param, &param_image);
if ( encoder == NULL ) {
fprintf(stderr, "Failed to create encoder!\n");
return -1;
}
// Encode images
for ( int index = 0; index < argc; index += 2 ) {
// Get and check input and output image
const char* input = argv[index];
const char* output = argv[index + 1];
enum gpujpeg_image_file_format input_format = gpujpeg_image_get_file_format(input);
enum gpujpeg_image_file_format output_format = gpujpeg_image_get_file_format(output);
if ( (input_format & GPUJPEG_IMAGE_FILE_RAW) == 0 ) {
fprintf(stderr, "[Warning] Encoder input file [%s] should be raw image (*.rgb, *.yuv, *.r)!\n", input);
if ( input_format & GPUJPEG_IMAGE_FILE_JPEG ) {
return -1;
}
}
if ( output_format != GPUJPEG_IMAGE_FILE_JPEG ) {
fprintf(stderr, "Encoder output file [%s] should be JPEG image (*.jpg)!\n", output);
return -1;
}
// Encode image
GPUJPEG_TIMER_INIT();
printf("\nEncoding Image [%s]\n", input);
GPUJPEG_TIMER_START();
// Load image
int image_size = gpujpeg_image_calculate_size(&param_image);
uint8_t* image = NULL;
if ( gpujpeg_image_load_from_file(input, &image, &image_size) != 0 ) {
fprintf(stderr, "Failed to load image [%s]!\n", argv[index]);
return -1;
}
GPUJPEG_TIMER_STOP();
printf("Load Image: %10.2f ms\n", GPUJPEG_TIMER_DURATION());
// Prepare encoder input
struct gpujpeg_encoder_input encoder_input;
if ( use_opengl ) {
gpujpeg_opengl_texture_set_data(texture->texture_id, image);
gpujpeg_encoder_input_set_texture(&encoder_input, texture);
} else {
gpujpeg_encoder_input_set_image(&encoder_input, image);
}
// Encode image
uint8_t* image_compressed = NULL;
int image_compressed_size = 0;
for ( int iteration = 0; iteration < iterate; iteration++ ) {
if ( iterate > 1 ) {
printf("\nIteration #%d:\n", iteration + 1);
}
GPUJPEG_TIMER_START();
if ( gpujpeg_encoder_encode(encoder, &encoder_input, &image_compressed, &image_compressed_size) != 0 ) {
fprintf(stderr, "Failed to encode image [%s]!\n", argv[index]);
return -1;
}
GPUJPEG_TIMER_STOP();
float duration = GPUJPEG_TIMER_DURATION();
if ( param.verbose ) {
printf(" -Copy To Device: %10.2f ms\n", encoder->coder.duration_memory_to);
if ( encoder->coder.duration_memory_map != 0.0 && encoder->coder.duration_memory_unmap != 0.0 ) {
printf(" -OpenGL Memory Map: %10.2f ms\n", encoder->coder.duration_memory_map);
printf(" -OpenGL Memory Unmap:%9.2f ms\n", encoder->coder.duration_memory_unmap);
}
printf(" -Preprocessing: %10.2f ms\n", encoder->coder.duration_preprocessor);
printf(" -DCT & Quantization:%10.2f ms\n", encoder->coder.duration_dct_quantization);
printf(" -Huffman Encoder: %10.2f ms\n", encoder->coder.duration_huffman_coder);
printf(" -Copy From Device: %10.2f ms\n", encoder->coder.duration_memory_from);
printf(" -Stream Formatter: %10.2f ms\n", encoder->coder.duration_stream);
}
printf("Encode Image GPU: %10.2f ms (only in-GPU processing)\n", encoder->coder.duration_in_gpu);
printf("Encode Image Bare: %10.2f ms (without copy to/from GPU memory)\n", duration - encoder->coder.duration_memory_to - encoder->coder.duration_memory_from);
printf("Encode Image: %10.2f ms\n", duration);
}
if ( iterate > 1 ) {
printf("\n");
}
GPUJPEG_TIMER_START();
// Save image
if ( gpujpeg_image_save_to_file(output, image_compressed, image_compressed_size) != 0 ) {
fprintf(stderr, "Failed to save image [%s]!\n", argv[index]);
return -1;
}
GPUJPEG_TIMER_STOP();
printf("Save Image: %10.2f ms\n", GPUJPEG_TIMER_DURATION());
printf("Compressed Size: %10.d bytes [%s]\n", image_compressed_size, output);
// Destroy image
gpujpeg_image_destroy(image);
}
// Destroy OpenGL texture
if ( use_opengl ) {
int texture_id = texture->texture_id;
gpujpeg_opengl_texture_unregister(texture);
gpujpeg_opengl_texture_destroy(texture_id);
}
// Destroy encoder
gpujpeg_encoder_destroy(encoder);
}
if ( decode == 1 ) {
// Create OpenGL texture
struct gpujpeg_opengl_texture* texture = NULL;
if ( use_opengl ) {
assert(param_image.sampling_factor == GPUJPEG_4_4_4);
int texture_id = gpujpeg_opengl_texture_create(param_image.width, param_image.height, NULL);
assert(texture_id != 0);
texture = gpujpeg_opengl_texture_register(texture_id, GPUJPEG_OPENGL_TEXTURE_WRITE);
assert(texture != NULL);
}
// Create decoder
struct gpujpeg_decoder* decoder = gpujpeg_decoder_create();
if ( decoder == NULL ) {
fprintf(stderr, "Failed to create decoder!\n");
return -1;
}
// Init decoder if image size is filled
if ( param_image.width != 0 && param_image.height != 0 ) {
if ( gpujpeg_decoder_init(decoder, &param, &param_image) != 0 ) {
fprintf(stderr, "Failed to preinitialize decoder!\n");
return -1;
}
} else {
decoder->coder.param_image.color_space = param_image.color_space;
decoder->coder.param_image.sampling_factor = param_image.sampling_factor;
}
// Decode images
for ( int index = 0; index < argc; index += 2 ) {
// Get and check input and output image
const char* input = argv[index];
const char* output = argv[index + 1];
if ( encode == 1 ) {
static char buffer_output[255];
if ( param_image.color_space != GPUJPEG_RGB ) {
sprintf(buffer_output, "%s.decoded.yuv", output);
}
else {
if ( param_image.comp_count == 1 ) {
sprintf(buffer_output, "%s.decoded.r", output);
} else {
sprintf(buffer_output, "%s.decoded.rgb", output);
}
}
input = output;
output = buffer_output;
}
enum gpujpeg_image_file_format input_format = gpujpeg_image_get_file_format(input);
enum gpujpeg_image_file_format output_format = gpujpeg_image_get_file_format(output);
if ( input_format != GPUJPEG_IMAGE_FILE_JPEG ) {
fprintf(stderr, "Decoder input file [%s] should be JPEG image (*.jpg)!\n", input);
return -1;
}
if ( (output_format & GPUJPEG_IMAGE_FILE_RAW) == 0 ) {
fprintf(stderr, "[Warning] Decoder output file [%s] should be raw image (*.rgb, *.yuv, *.r)!\n", output);
if ( output_format & GPUJPEG_IMAGE_FILE_JPEG ) {
return -1;
}
}
// Decode image
GPUJPEG_TIMER_INIT();
GPUJPEG_TIMER_START();
printf("\nDecoding Image [%s]\n", input);
// Load image
int image_size = 0;
uint8_t* image = NULL;
if ( gpujpeg_image_load_from_file(input, &image, &image_size) != 0 ) {
fprintf(stderr, "Failed to load image [%s]!\n", argv[index]);
return -1;
}
GPUJPEG_TIMER_STOP();
printf("Load Image: %10.2f ms\n", GPUJPEG_TIMER_DURATION());
// Prepare decoder output buffer
struct gpujpeg_decoder_output decoder_output;
if ( use_opengl ) {
gpujpeg_decoder_output_set_texture(&decoder_output, texture);
} else {
gpujpeg_decoder_output_set_default(&decoder_output);
}
for ( int iteration = 0; iteration < iterate; iteration++ ) {
if ( iterate > 1 ) {
printf("\nIteration #%d:\n", iteration + 1);
}
GPUJPEG_TIMER_START();
// Decode image
if ( gpujpeg_decoder_decode(decoder, image, image_size, &decoder_output) != 0 ) {
fprintf(stderr, "Failed to decode image [%s]!\n", argv[index]);
return -1;
}
GPUJPEG_TIMER_STOP();
float duration = GPUJPEG_TIMER_DURATION();
if ( param.verbose ) {
printf(" -Stream Reader: %10.2f ms\n", decoder->coder.duration_stream);
printf(" -Copy To Device: %10.2f ms\n", decoder->coder.duration_memory_to);
printf(" -Huffman Decoder: %10.2f ms\n", decoder->coder.duration_huffman_coder);
printf(" -DCT & Quantization:%10.2f ms\n", decoder->coder.duration_dct_quantization);
printf(" -Postprocessing: %10.2f ms\n", decoder->coder.duration_preprocessor);
printf(" -Copy From Device: %10.2f ms\n", decoder->coder.duration_memory_from);
if ( decoder->coder.duration_memory_map != 0.0 && decoder->coder.duration_memory_unmap != 0.0 ) {
printf(" -OpenGL Memory Map: %10.2f ms\n", decoder->coder.duration_memory_map);
printf(" -OpenGL Memory Unmap:%9.2f ms\n", decoder->coder.duration_memory_unmap);
}
}
printf("Decode Image GPU: %10.2f ms (only in-GPU processing)\n", decoder->coder.duration_in_gpu);
printf("Decode Image Bare: %10.2f ms (without copy to/from GPU memory)\n", duration - decoder->coder.duration_memory_to - decoder->coder.duration_memory_from);
printf("Decode Image: %10.2f ms\n", duration);
}
if ( iterate > 1 ) {
printf("\n");
}
uint8_t* data = NULL;
int data_size = 0;
if ( use_opengl ) {
gpujpeg_opengl_texture_get_data(texture->texture_id, decoder->coder.data_raw, &data_size);
data = decoder->coder.data_raw;
assert(data != NULL && data_size != 0);
} else {
data = decoder_output.data;
data_size = decoder_output.data_size;
}
GPUJPEG_TIMER_START();
// Save image
if ( gpujpeg_image_save_to_file(output, data, data_size) != 0 ) {
fprintf(stderr, "Failed to save image [%s]!\n", argv[index]);
return -1;
}
GPUJPEG_TIMER_STOP();
printf("Save Image: %10.2f ms\n", GPUJPEG_TIMER_DURATION());
printf("Decompressed Size: %10.d bytes [%s]\n", decoder_output.data_size, output);
// Destroy image
gpujpeg_image_destroy(image);
}
// Destroy OpenGL texture
if ( use_opengl ) {
int texture_id = texture->texture_id;
gpujpeg_opengl_texture_unregister(texture);
gpujpeg_opengl_texture_destroy(texture_id);
}
// Destroy decoder
gpujpeg_decoder_destroy(decoder);
}
return 0;
}