Compare commits

...

11 Commits
v1.0 ... v1.0.1

Author SHA1 Message Date
Frank Bossen
0c7449c461 Fix #25
Scan pattern needs to be set again after changing colorspace
2014-03-27 14:31:35 +01:00
Frank Bossen
2906cbf6ed Fix issue with jpegtran and RGB/CMYK colorspaces
Fixes issues #23 #24 #31
Note that the original jpgcrush script optimizes scans only for YCbCr
and grayscale color spaces. Scan optimization is thus disabled for RGB
and CMYK color spaces and behavior reverts to the fast mode of jpgcrush
which uses predefined scans
2014-03-27 11:58:24 +01:00
Josh Aas
7b66e2fa25 Merge pull request #26 from negge/rd
Adding scripts to generate rd-curves.
2014-03-24 14:07:29 -05:00
Nathan E. Egge
5d332e6447 Adding scripts to generate rd-curves. 2014-03-17 19:30:43 -04:00
Josh Aas
8d2a67c7dd Merge pull request #6 from negge/yuv
Adding yuvjpeg and jpegyuv utilities.
2014-03-13 10:25:05 -05:00
Josh Aas
a4871a16c6 Add link to announcement blog post in README. 2014-03-13 10:21:32 -05:00
Josh Aas
e59db4fad5 Add license file to top-level dir. 2014-03-13 10:18:12 -05:00
Josh Aas
d12ca848a2 Windows build fix. 2014-03-13 10:01:11 -05:00
Josh Aas
030a805b5b Update project README to state goals and clarify re: general JPEG library use. 2014-03-11 09:03:53 -05:00
Josh Aas
0c48e5367b Compile fix. 2014-03-07 16:12:04 +00:00
Nathan E. Egge
7448ab4e82 Adding yuvjpeg and jpegyuv utilities. 2014-03-05 14:01:25 -05:00
13 changed files with 572 additions and 7 deletions

View File

@@ -485,7 +485,7 @@ add_custom_target(installer
makensis -nocd ${INST_DEFS} libmozjpeg.nsi
DEPENDS jpeg jpeg-static turbojpeg turbojpeg-static rdjpgcom wrjpgcom
cjpeg djpeg jpegtran tjbench ${JAVA_DEPEND}
SOURCES libjpeg-turbo.nsi)
SOURCES libmozjpeg.nsi)
install(TARGETS jpeg-static turbojpeg turbojpeg-static rdjpgcom wrjpgcom tjbench
ARCHIVE DESTINATION lib

7
LICENSE.txt Normal file
View File

@@ -0,0 +1,7 @@
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.
* Neither the name of the libmozjpeg Project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
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 HOLDERS 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.

View File

@@ -84,7 +84,7 @@ endif
bin_PROGRAMS = cjpeg djpeg jpegtran rdjpgcom wrjpgcom
noinst_PROGRAMS = jcstest
noinst_PROGRAMS = jcstest jpegyuv yuvjpeg
if WITH_TURBOJPEG
@@ -139,6 +139,14 @@ jcstest_SOURCES = jcstest.c
jcstest_LDADD = libjpeg.la
jpegyuv_SOURCES = jpegyuv.c
jpegyuv_LDADD = libjpeg.la
yuvjpeg_SOURCES = yuvjpeg.c
yuvjpeg_LDADD = libjpeg.la
dist_man1_MANS = cjpeg.1 djpeg.1 jpegtran.1 rdjpgcom.1 wrjpgcom.1
DOCS= coderules.txt jconfig.txt change.log rdrle.c wrrle.c BUILDING.txt \

View File

@@ -1 +1,12 @@
Mozilla JPEG encoder/decoder project.
Mozilla JPEG Encoder Project
============================
This project's goal is to reduce the size of JPEG files without reducing quality or compatibility with the vast majority of the world's deployed decoders.
The idea is to reduce transfer times for JPEGs on the Web, thus reducing page load times.
'mozjpeg' is not intended to be a general JPEG library replacement. It makes tradeoffs that are intended to benefit Web use cases and focuses solely on improving encoding. It is best used as part of a Web encoding workflow. For a general JPEG library (e.g. your system libjpeg), especially if you care about decoding, we recommend libjpeg-turbo.
For more information, see the project announcement:
https://blog.mozilla.org/research/2014/03/05/introducing-the-mozjpeg-project/

View File

@@ -741,20 +741,21 @@ jpeg_search_progression (j_compress_ptr cinfo)
GLOBAL(void)
jpeg_simple_progression (j_compress_ptr cinfo)
{
int ncomps;
int nscans;
jpeg_scan_info * scanptr;
if (cinfo->optimize_scans) {
if (jpeg_search_progression(cinfo) == TRUE)
return;
}
int ncomps = cinfo->num_components;
int nscans;
jpeg_scan_info * scanptr;
/* Safety check to ensure start_compress not called yet. */
if (cinfo->global_state != CSTATE_START)
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
/* Figure space needed for script. Calculation must match code below! */
ncomps = cinfo->num_components;
if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) {
/* Custom script for YCbCr color images. */
nscans = 10;

View File

@@ -38,6 +38,10 @@ LOCAL(void) transencode_coef_controller
GLOBAL(void)
jpeg_write_coefficients (j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)
{
/* setting up scan optimisation pattern failed, disable scan optimisation */
if (cinfo->num_scans_luma == 0)
cinfo->optimize_scans = FALSE;
if (cinfo->global_state != CSTATE_START)
ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state);
/* Mark all tables to be written */

138
jpegyuv.c Normal file
View File

@@ -0,0 +1,138 @@
/*
* Written by Josh Aas and Tim Terriberry
* Copyright (c) 2013, Mozilla Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. 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.
* 3. Neither the name of the Mozilla Corporation nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 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.
*/
/* Input: JPEG YUV 4:2:0 */
/* Output: YUV 4:2:0 */
/* gcc -std=c99 jpegyuv.c -I/opt/local/include/ -L/opt/local/lib/ -ljpeg -o jpegyuv */
#include <errno.h>
#include <stdio.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include "jpeglib.h"
int main(int argc, char *argv[]) {
const char *jpg_path;
const char *yuv_path;
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE *jpg_fd;
int width;
int height;
int yuv_size;
JSAMPLE *image_buffer;
JSAMPROW yrow_pointer[16];
JSAMPROW cbrow_pointer[8];
JSAMPROW crrow_pointer[8];
JSAMPROW *plane_pointer[3];
int y;
FILE *yuv_fd;
if (argc != 3) {
fprintf(stderr, "Required arguments:\n");
fprintf(stderr, "1. Path to JPG input file\n");
fprintf(stderr, "2. Path to YUV output file\n");
return 1;
}
errno = 0;
/* Will check these for validity when opening via 'fopen'. */
jpg_path = argv[1];
yuv_path = argv[2];
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpg_fd = fopen(jpg_path, "rb");
if (!jpg_fd) {
fprintf(stderr, "Invalid path to JPEG file!\n");
return 1;
}
jpeg_stdio_src(&cinfo, jpg_fd);
jpeg_read_header(&cinfo, TRUE);
cinfo.raw_data_out = TRUE;
cinfo.do_fancy_upsampling = FALSE;
jpeg_start_decompress(&cinfo);
width = cinfo.output_width;
height = cinfo.output_height;
/* Right now we only support dimensions that are multiples of 16. */
if ((width % 16) != 0 || (height % 16) != 0) {
fprintf(stderr, "Image dimensions must be multiples of 16!\n");
return 1;
}
yuv_size = (width * height) + (((width >> 1) * (height >> 1)) * 2);
image_buffer = malloc(yuv_size);
plane_pointer[0] = yrow_pointer;
plane_pointer[1] = cbrow_pointer;
plane_pointer[2] = crrow_pointer;
while (cinfo.output_scanline < cinfo.output_height) {
for (y = 0; y < 16; y++) {
yrow_pointer[y] = image_buffer + cinfo.image_width * (cinfo.output_scanline + y);
}
for (y = 0; y < 8; y++) {
cbrow_pointer[y] = image_buffer + width * height +
((width + 1) >> 1) * ((cinfo.output_scanline >> 1) + y);
crrow_pointer[y] = image_buffer + width * height +
((width + 1) >> 1) * ((height + 1) >> 1) +
((width + 1) >> 1) * ((cinfo.output_scanline >> 1) + y);
}
jpeg_read_raw_data(&cinfo, plane_pointer, 16);
}
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
fclose(jpg_fd);
yuv_fd = fopen(yuv_path, "wb");
if (!yuv_fd) {
fprintf(stderr, "Invalid path to YUV file!");
return 1;
}
if (fwrite(image_buffer, yuv_size, 1, yuv_fd)!=1) {
fprintf(stderr, "Error writing yuv file\n");
}
fclose(yuv_fd);
return 0;
}

15
rd_average.sh Executable file
View File

@@ -0,0 +1,15 @@
#!/bin/bash
set -e
if [ $# == 0 ]; then
echo "usage: OUTPUT=<label> $0 *.out"
exit 1
fi
TOTAL=total.out
if [ -n "$OUTPUT" ]; then
TOTAL="$OUTPUT.out"
fi
awk '{size[FNR]+=$2;bytes[FNR]+=$3;psnr[FNR]+=$2*$4;psnrhvs[FNR]+=$2*$5;ssim[FNR]+=$2*$6;fastssim[FNR]+=$2*$7;}END{for(i=1;i<=FNR;i++)print i-1,size[i],bytes[i],psnr[i]/size[i],psnrhvs[i]/size[i],ssim[i]/size[i],fastssim[i]/size[i];}' $@ > $TOTAL

106
rd_collect.sh Executable file
View File

@@ -0,0 +1,106 @@
#!/bin/bash
set -e
if [ $# == 0 ]; then
echo "usage: DAALA_ROOT=<daala_root> MOZJPEG_ROOT=<mozjpeg_root> $0 *.y4m"
exit 1
fi
if [ -z $MOZJPEG_ROOT ]; then
MOZJPEG_ROOT=.
fi
if [ -z $DAALA_ROOT ]; then
echo "DAALA_ROOT not set."
exit 1
fi
if [ -z "$PLANE" ]; then
export PLANE=0
fi
if [ $PLANE != 0 ] && [ $PLANE != 1 ] && [ $PLANE != 2 ]; then
echo "Invalid plane $PLANE. Must be 0, 1 or 2."
exit 1
fi
if [ -z "$YUVJPEG" ]; then
export YUVJPEG=$MOZJPEG_ROOT/yuvjpeg
fi
if [ -z "$JPEGYUV" ]; then
export JPEGYUV=$MOZJPEG_ROOT/jpegyuv
fi
if [ ! -x "$YUVJPEG" ]; then
echo "Executable not found YUVJPEG=$YUVJPEG"
echo "Do you have the right MOZJPEG_ROOT=$MOZJPEG_ROOT"
exit 1
fi
if [ ! -x "$JPEGYUV" ]; then
echo "Executable not found JPEGYUV=$JPEGYUV"
echo "Do you have the right MOZJPEG_ROOT=$MOZJPEG_ROOT"
exit 1
fi
# TODO refactor these out of the daala project into a metrics project
if [ -z "$YUV2YUV4MPEG" ]; then
export YUV2YUV4MPEG=$DAALA_ROOT/tools/yuv2yuv4mpeg
fi
if [ -z "$DUMP_PSNR" ]; then
export DUMP_PSNR=$DAALA_ROOT/tools/dump_psnr
fi
if [ -z "$DUMP_PSNRHVS" ]; then
export DUMP_PSNRHVS=$DAALA_ROOT/tools/dump_psnrhvs
fi
if [ -z "$DUMP_SSIM" ]; then
export DUMP_SSIM=$DAALA_ROOT/tools/dump_ssim
fi
if [ -z "$DUMP_FASTSSIM" ]; then
export DUMP_FASTSSIM=$DAALA_ROOT/tools/dump_fastssim
fi
if [ ! -x "$YUV2YUV4MPEG" ]; then
echo "Executable not found YUV2YUV4MPEG=$YUV2YUV4MPEG"
echo "Do you have the right DAALA_ROOT=$DAALA_ROOT"
exit 1
fi
if [ ! -x "$DUMP_PSNR" ]; then
echo "Executable not found DUMP_PSNR=$DUMP_PSNR"
echo "Do you have the right DAALA_ROOT=$DAALA_ROOT"
exit 1
fi
if [ ! -x "$DUMP_PSNRHVS" ]; then
echo "Executable not found DUMP_PSNRHVS=$DUMP_PSNRHVS"
echo "Do you have the right DAALA_ROOT=$DAALA_ROOT"
exit 1
fi
if [ ! -x "$DUMP_SSIM" ]; then
echo "Executable not found DUMP_SSIM=$DUMP_SSIM"
echo "Do you have the right DAALA_ROOT=$DAALA_ROOT"
exit 1
fi
if [ ! -x "$DUMP_FASTSSIM" ]; then
echo "Executable not found DUMP_FASTSSIM=$DUMP_FASTSSIM"
echo "Do you have the right DAALA_ROOT=$DAALA_ROOT"
exit 1
fi
RD_COLLECT_SUB=$(dirname "$0")/rd_collect_sub.sh
if [ -z "$CORES" ]; then
CORES=`grep -i processor /proc/cpuinfo | wc -l`
#echo "CORES not set, using $CORES"
fi
find $@ -type f -name "*.y4m" -print0 | xargs -0 -n1 -P$CORES $RD_COLLECT_SUB

28
rd_collect_sub.sh Executable file
View File

@@ -0,0 +1,28 @@
#!/bin/bash
set -e
FILE=$1
BASENAME=$(basename $FILE)
rm $BASENAME.out 2> /dev/null || true
echo $BASENAME
tail -n+3 $FILE > $BASENAME-in.yuv
WIDTH=$(head -1 $FILE | cut -d\ -f 2 | tr -d 'W')
HEIGHT=$(head -1 $FILE | cut -d\ -f 3 | tr -d 'H')
for x in {0..100}; do
$YUVJPEG $x "$WIDTH"x$HEIGHT $BASENAME-in.yuv $BASENAME.jpeg
$JPEGYUV $BASENAME.jpeg $BASENAME.yuv
$YUV2YUV4MPEG $BASENAME -w$WIDTH -h$HEIGHT -an0 -ad0 -c420mpeg2
PIXELS=$(($WIDTH*$HEIGHT))
SIZE=$(wc -c $BASENAME.jpeg | awk '{ print $1 }')
PSNR=$($DUMP_PSNR $FILE $BASENAME.y4m 2> /dev/null | grep Total | tr -s ' ' | cut -d\ -f $((4+$PLANE*2)))
PSNRHVS=$($DUMP_PSNRHVS $FILE $BASENAME.y4m 2> /dev/null | grep Total | tr -s ' ' | cut -d\ -f $((4+$PLANE*2)))
SSIM=$($DUMP_SSIM $FILE $BASENAME.y4m 2> /dev/null | grep Total | tr -s ' ' | cut -d\ -f $((4+$PLANE*2)))
FASTSSIM=$($DUMP_FASTSSIM -c $FILE $BASENAME.y4m 2> /dev/null | grep Total | tr -s ' ' | cut -d\ -f $((4+$PLANE*2)))
rm $BASENAME.jpeg $BASENAME.yuv $BASENAME.y4m
echo $x $PIXELS $SIZE $PSNR $PSNRHVS $SSIM $FASTSSIM >> $BASENAME.out
#tail -1 $BASENAME.out
done
rm $BASENAME-in.yuv

47
rd_plot.sh Executable file
View File

@@ -0,0 +1,47 @@
#!/bin/bash
set -e
# Use this to average data from multiple runs
#awk '{size[FNR]+=$2;bytes[FNR]+=$3;psnr[FNR]+=$2*$4;psnrhvs[FNR]+=$2*$5;ssim[FNR]+=$2*$6;fastssim[FNR]+=$2*$7;}END{for(i=1;i<=FNR;i++)print i+1,size[i],bytes[i],psnr[i]/size[i],psnrhvs[i]/size[i],ssim[i]/size[i],fastssim[i]/size[i];}' *.out > total.out
if [ -n "$IMAGE" ]; then
IMAGE="$IMAGE-"
fi
if [ $# == 0 ]; then
echo "usage: IMAGE=<prefix> $0 *.out"
exit 1
fi
if [ -z "$GNUPLOT" -a -n "`type -p gnuplot`" ]; then
GNUPLOT=`type -p gnuplot`
fi
if [ ! -x "$GNUPLOT" ]; then
echo "Executable not found GNUPLOT=$GNUPLOT"
echo "Please install it or set GNUPLOT to point to an installed copy"
exit 1
fi
CMDS="$CMDS set term pngcairo dashed size 1024,768;"
CMDS="$CMDS set log x;"
CMDS="$CMDS set xlabel 'Bits/Pixel';"
CMDS="$CMDS set ylabel 'dB';"
CMDS="$CMDS set key bot right;"
for FILE in "$@"; do
BASENAME=$(basename $FILE)
PSNR="$PSNR $PREFIX '$FILE' using (\$3*8/\$2):4 with lines title '${BASENAME%.*} (PSNR)'"
PSNRHVS="$PSNRHVS $PREFIX '$FILE' using (\$3*8/\$2):5 with lines title '${BASENAME%.*} (PSNR-HVS)'"
SSIM="$SSIM $PREFIX '$FILE' using (\$3*8/\$2):6 with lines title '${BASENAME%.*} (SSIM)'"
FASTSSIM="$FASTSSIM $PREFIX '$FILE' using (\$3*8/\$2):7 with lines title '${BASENAME%.*} (FAST SSIM)'"
PREFIX=","
done
SUFFIX="psnr.png"
$GNUPLOT -e "$CMDS set output \"$IMAGE$SUFFIX\"; plot $PSNR;" 2> /dev/null
SUFFIX="psnrhvs.png"
$GNUPLOT -e "$CMDS set output \"$IMAGE$SUFFIX\"; plot $PSNRHVS;" 2> /dev/null
SUFFIX="ssim.png"
$GNUPLOT -e "$CMDS set output \"$IMAGE$SUFFIX\"; plot $SSIM;" 2> /dev/null
SUFFIX="fastssim.png"
$GNUPLOT -e "$CMDS set output \"$IMAGE$SUFFIX\"; plot $FASTSSIM;" 2> /dev/null

View File

@@ -208,6 +208,10 @@ static int setCompDefaults(struct jpeg_compress_struct *cinfo,
jpeg_set_colorspace(cinfo, JCS_YCCK);
else jpeg_set_colorspace(cinfo, JCS_YCbCr);
/* Set scan pattern again as colorspace might have changed */
if (cinfo->use_moz_defaults)
jpeg_simple_progression(cinfo);
cinfo->comp_info[0].h_samp_factor=tjMCUWidth[subsamp]/8;
cinfo->comp_info[1].h_samp_factor=1;
cinfo->comp_info[2].h_samp_factor=1;

196
yuvjpeg.c Normal file
View File

@@ -0,0 +1,196 @@
/*
* Written by Josh Aas and Tim Terriberry
* Copyright (c) 2013, Mozilla Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. 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.
* 3. Neither the name of the Mozilla Corporation nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 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.
*/
/* Expects 4:2:0 YCbCr */
/* gcc -std=c99 yuvjpeg.c -I/opt/local/include/ -L/opt/local/lib/ -ljpeg -o yuvjpeg */
#include <errno.h>
#include <stdio.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include "jpeglib.h"
int main(int argc, char *argv[]) {
long quality;
const char *size;
char *x;
long width;
long height;
const char *yuv_path;
const char *jpg_path;
FILE *yuv_fd;
size_t yuv_size;
JSAMPLE *image_buffer;
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE *jpg_fd;
JSAMPROW yrow_pointer[16];
JSAMPROW cbrow_pointer[8];
JSAMPROW crrow_pointer[8];
JSAMPROW *plane_pointer[3];
int y;
if (argc != 5) {
fprintf(stderr, "Required arguments:\n");
fprintf(stderr, "1. JPEG quality value, 0-100\n");
fprintf(stderr, "2. Image size (e.g. '512x512'\n");
fprintf(stderr, "3. Path to YUV input file\n");
fprintf(stderr, "4. Path to JPG output file\n");
return 1;
}
errno = 0;
quality = strtol(argv[1], NULL, 10);
if (errno != 0 || quality < 0 || quality > 100) {
fprintf(stderr, "Invalid JPEG quality value!\n");
return 1;
}
size = argv[2];
x = strchr(size, 'x');
if (!x && x != size && x != (x + strlen(x) - 1)) {
fprintf(stderr, "Invalid image size input!\n");
return 1;
}
width = strtol(size, NULL, 10);
if (errno != 0) {
fprintf(stderr, "Invalid image size input!\n");
return 1;
}
height = strtol(x + 1, NULL, 10);
if (errno != 0) {
fprintf(stderr, "Invalid image size input!\n");
return 1;
}
/* Right now we only support dimensions that are multiples of 16. */
if ((width % 16) != 0 || (height % 16) != 0) {
fprintf(stderr, "Image dimensions must be multiples of 16!\n");
return 1;
}
/* Will check these for validity when opening via 'fopen'. */
yuv_path = argv[3];
jpg_path = argv[4];
yuv_fd = fopen(yuv_path, "r");
if (!yuv_fd) {
fprintf(stderr, "Invalid path to YUV file!\n");
return 1;
}
fseek(yuv_fd, 0, SEEK_END);
yuv_size = ftell(yuv_fd);
fseek(yuv_fd, 0, SEEK_SET);
image_buffer = malloc(yuv_size);
if (fread(image_buffer, yuv_size, 1, yuv_fd)!=1) {
fprintf(stderr, "Error reading yuv file\n");
};
fclose(yuv_fd);
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
jpg_fd = fopen(jpg_path, "wb");
if (!jpg_fd) {
fprintf(stderr, "Invalid path to JPEG file!\n");
return 1;
}
jpeg_stdio_dest(&cinfo, jpg_fd);
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_YCbCr;
cinfo.use_moz_defaults = TRUE;
jpeg_set_defaults(&cinfo);
cinfo.raw_data_in = TRUE;
#if JPEG_LIB_VERSION >= 70
cinfo.do_fancy_downsampling = FALSE;
#endif
cinfo.comp_info[0].h_samp_factor = 2;
cinfo.comp_info[0].v_samp_factor = 2;
cinfo.comp_info[0].dc_tbl_no = 0;
cinfo.comp_info[0].ac_tbl_no = 0;
cinfo.comp_info[0].quant_tbl_no = 0;
cinfo.comp_info[1].h_samp_factor = 1;
cinfo.comp_info[1].v_samp_factor = 1;
cinfo.comp_info[1].dc_tbl_no = 1;
cinfo.comp_info[1].ac_tbl_no = 1;
cinfo.comp_info[1].quant_tbl_no = 1;
cinfo.comp_info[2].h_samp_factor = 1;
cinfo.comp_info[2].v_samp_factor = 1;
cinfo.comp_info[2].dc_tbl_no = 1;
cinfo.comp_info[2].ac_tbl_no = 1;
cinfo.comp_info[2].quant_tbl_no = 1;
jpeg_set_quality(&cinfo, quality, TRUE);
cinfo.optimize_coding = TRUE;
jpeg_start_compress(&cinfo, TRUE);
plane_pointer[0] = yrow_pointer;
plane_pointer[1] = cbrow_pointer;
plane_pointer[2] = crrow_pointer;
while (cinfo.next_scanline < cinfo.image_height) {
for (y = 0; y < 16; y++) {
yrow_pointer[y] = image_buffer + cinfo.image_width * (cinfo.next_scanline + y);
}
for (y = 0; y < 8; y++) {
cbrow_pointer[y] = image_buffer + width * height +
((width + 1) >> 1) * ((cinfo.next_scanline >> 1) + y);
crrow_pointer[y] = image_buffer + width * height +
((width + 1) >> 1) * ((height + 1) >> 1) +
((width + 1) >> 1) * ((cinfo.next_scanline >> 1) + y);
}
jpeg_write_raw_data(&cinfo, plane_pointer, 16);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
free(image_buffer);
fclose(jpg_fd);
return 0;
}