blob: 976767cc62f701f635473cd51eb257cd42fbf761 [file] [log] [blame]
/* AUTORIGHTS
Copyright (C) 2007 Princeton University
This file is part of Ferret Toolkit.
Ferret Toolkit 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, or (at your option)
any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <jpeglib.h>
#include "image.h"
#define DEFAULT_SIZE 128
#define EPSILON 1e-6F
#define RESIZE_FILTER_SUPPORT 3.0F
static inline float sinc(float x)
{
if (x == 0.0)
return(1.0);
return(sin(M_PI*(double)x)/(M_PI*(double) x));
}
static inline float weight(float x)
{
x=fabs(x);
return(sinc(x/RESIZE_FILTER_SUPPORT)*sinc(x));
}
static inline double Max(double x,double y)
{
return x > y ? x : y;
}
static inline double Min(double x,double y)
{
return y > x ? x : y;
}
static inline unsigned char myround (float v) {
if (v <= 0) return 0;
if (v >= 255) return 255;
return v + 0.5;
}
int horizontal(const unsigned char *image, int orig_width, int orig_height, unsigned char *resize_image, int width)
{
float *contrib;
float factor=(float)width/(float)orig_width;
float scale=Max(1.0/factor,1.0);
float support=scale*RESIZE_FILTER_SUPPORT;
long x;
if (support < 0.5) // sampling
{
support=(float) 0.5;
scale=1.0;
}
contrib=(float *)malloc((size_t) (2.0*support+3.0) * sizeof(float));
if (contrib == NULL) fatal("out of memory");
scale=1.0/scale;
for (x=0; x < (long) width; x++)
{
long i, n, start, stop;
float center, density;
register long y;
center=(float) (x+0.5)/factor;
start=(long) (Max(center-support-EPSILON,0.0)+0.5);
stop=(long) (Min(center+support,(double) orig_width)+0.5);
density=0.0;
for (n=0; n < (stop-start); n++)
{
contrib[n]=weight(scale*((float)(start+n)-center+0.5));
density+=contrib[n];
}
for (i=0; i < n; i++) {
contrib[i]/=density;
}
for (y=0; y < (long) orig_height; y++)
{
const unsigned char *p = image + CHAN * (y * orig_width + start);
unsigned char *q = resize_image + CHAN * (y * width + x);
float r = 0, g = 0, b = 0;
for (i=0; i < n; i++)
{
float alpha =contrib[i];
r += alpha * *p++;
g += alpha * *p++;
b += alpha * *p++;
}
*q++ = myround(r);
*q++ = myround(g);
*q++ = myround(b);
}
}
free(contrib);
return 0;
}
int vertical(const unsigned char *image, int orig_width, int orig_height, unsigned char *resize_image, int height) {
long y;
float *contrib;
float factor=(float)height/(float)orig_height;
float scale=Max(1.0/factor,1.0);
float support=scale*RESIZE_FILTER_SUPPORT;
if (support < 0.5) // sampling
{
support=(float) 0.5;
scale=1.0;
}
contrib=(float *)malloc((size_t) (2.0*support+3.0) * sizeof(float));
if (contrib == NULL) fatal("out of memory");
scale=1.0/scale;
for (y=0; y < (long) height; y++)
{
long i, n, start, stop;
float center, density;
register long x;
center=(float) (y+0.5)/factor;
start=(long) (Max(center-support-EPSILON,0.0)+0.5);
stop=(long) (Min(center+support,(double) orig_height)+0.5);
density=0.0;
for (n=0; n < (stop-start); n++)
{
contrib[n]=weight(scale*((float)(start+n)-center+0.5));
density+=contrib[n];
}
for (i=0; i < n; i++) {
contrib[i]/=density;
}
for (x=0; x < (long) orig_width; x++)
{
const unsigned char *p = image + CHAN * (start * orig_width + x);
unsigned char *q = resize_image + CHAN * (y * orig_width + x);
float r = 0, g = 0, b = 0;
for (i=0; i < n; i++)
{
float alpha =contrib[i];
r += alpha * *p;
g += alpha * *(p+1);
b += alpha * *(p+2);
p += orig_width * CHAN;
}
*q++ = myround(r);
*q++ = myround(g);
*q++ = myround(b);
}
}
free(contrib);
return 0;
}
unsigned char *resize (unsigned char *image, int orig_width, int orig_height, int width, int height)
{
unsigned char *filter_image, *resize_image;
if (width * orig_height > height * orig_width) {
filter_image=(unsigned char *)malloc(width * orig_height * CHAN);
resize_image=(unsigned char *)malloc(width * height * CHAN);
if ((filter_image == NULL) || (resize_image == NULL)) fatal("out of memory");
horizontal(image, orig_width, orig_height, filter_image, width);
vertical(filter_image, width, orig_height, resize_image, height);
}
else {
filter_image=(unsigned char *)malloc(orig_width * height * CHAN);
resize_image=(unsigned char *)malloc(width * height * CHAN);
if ((filter_image == NULL) || (resize_image == NULL)) fatal("out of memory");
vertical(image, orig_width, orig_height, filter_image, height);
horizontal(filter_image, orig_width, height, resize_image, width);
}
free(filter_image);
return resize_image;
}
int image_init (const char *path)
{
return 0;
}
int image_cleanup (void)
{
return 0;
}
void pixel_rgb2hsv (const unsigned char *rgb, unsigned char *hsv)
{
unsigned char r = rgb[0];
unsigned char g = rgb[1];
unsigned char b = rgb[2];
float h = 0, s = 0, v = 0;
unsigned char delta = 0;
unsigned char mn = r, mx = r;
hsv[0] = hsv[1] = hsv[2] = 0;
if (g > mx) { mx = g; }
if (g < mn) { mn = g; }
if (b > mx) { mx = b;}
if (b < mn) { mn = b;}
delta = mx - mn;
hsv[2] = mx; // V
if (mx == 0) return;
hsv[1] = (unsigned)delta * 255 / (unsigned)mx;
if (delta == 0) return;
float hue = 0;
if (mx == r) {
hue = ((float)g - (float)b) / (float)delta;
}
else if (mx == g) {
hue = 2.0 + ((float)b - (float)r) / (float)delta;
}
else {
hue = 4.0 + ((float)r - (float)g) / (float)delta;
}
if (hue < 0) hue += 6.0;
hsv[0] = 255 * hue / 6.0;
}
void pixel_hsv2rgb (const unsigned char *hsv, unsigned char *rgb)
{
unsigned char h = hsv[0];
unsigned char s = hsv[1];
unsigned char v = hsv[2];
rgb[0] = rgb[1] = rgb[2];
if (s == 0) {
rgb[0] = rgb[1] = rgb[2] = v;
}
float hue = h * 6.0 / 255;
float f = hue - floor(hue);
unsigned p = v * (255.0 - s) / 255;
unsigned q = v * (255.0 - s * f) / 255;
unsigned t = v * (255.0 - s * (1.0 - f)) / 255;
switch ((int)hue) {
case 0:
default:
rgb[0] = v;
rgb[1] = t;
rgb[2] = p;
break;
case 1:
rgb[0] = q;
rgb[1] = v;
rgb[2] = p;
break;
case 2:
rgb[0] = p;
rgb[1] = v;
rgb[2] = t;
break;
case 3:
rgb[0] = p;
rgb[1] = q;
rgb[2] = v;
break;
case 4:
rgb[0] = t;
rgb[1] = p;
rgb[2] = v;
break;
case 5:
rgb[0] = v;
rgb[1] = p;
rgb[2] = q;
break;
}
}
void rgb2hsv (const unsigned char *rgb, int width, int height, unsigned char *hsv) {
int i;
for (i = 0; i < width * height; i++) {
pixel_rgb2hsv(rgb, hsv);
rgb += CHAN;
hsv += CHAN;
}
}
void hsv2rgb (const unsigned char *hsv, int width, int height, unsigned char *rgb) {
int i;
for (i = 0; i < width * height; i++) {
pixel_hsv2rgb(hsv, rgb);
rgb += CHAN;
hsv += CHAN;
}
}
/*
* Sample routine for JPEG decompression. We assume that the source file name
* is passed in. We want to return 1 on success, 0 on error.
*/
int image_read_rgb_hsv (const char *filename, int *width, int *height, unsigned char **data_rgb, unsigned char **data_hsv)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE * infile; /* source file */
unsigned char *orig;
unsigned char *rgb; /* Output row buffer */
unsigned char *hsv;
JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
int row_stride; /* physical row width in output buffer */
if ((infile = fopen(filename, "rb")) == NULL) {
fprintf(stderr, "can't open %s\n", filename);
return 1;
}
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, infile);
(void) jpeg_read_header(&cinfo, TRUE);
(void) jpeg_start_decompress(&cinfo);
row_stride = cinfo.output_width * cinfo.output_components;
orig = (unsigned char *)malloc(cinfo.output_width * cinfo.output_height * cinfo.output_components);
if (orig == NULL) fatal("out of memory");
row_pointer[0] = orig;
while (cinfo.output_scanline < cinfo.output_height) {
(void) jpeg_read_scanlines(&cinfo, row_pointer, 1);
row_pointer[0] += row_stride;
}
(void) jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
fclose(infile);
rgb = resize(orig, cinfo.output_width, cinfo.output_height, DEFAULT_SIZE, DEFAULT_SIZE);
hsv = (unsigned char *)malloc(DEFAULT_SIZE * DEFAULT_SIZE * CHAN);
rgb2hsv(rgb, DEFAULT_SIZE, DEFAULT_SIZE, hsv);
free(orig);
*width = DEFAULT_SIZE;
*height = DEFAULT_SIZE;
*data_rgb = rgb;
*data_hsv = hsv;
return 0;
}
int image_write_rgb (const char *filename, int width, int height, unsigned char *data)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE * outfile; /* target file */
JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
int row_stride; /* physical row width in image buffer */
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
if ((outfile = fopen(filename, "wb")) == NULL) {
fprintf(stderr, "can't open %s\n", filename);
exit(1);
}
jpeg_stdio_dest(&cinfo, outfile);
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
jpeg_start_compress(&cinfo, TRUE);
row_stride = width * 3;
row_pointer[0] = data;
while (cinfo.next_scanline < cinfo.image_height) {
(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
row_pointer[0] += row_stride;
}
jpeg_finish_compress(&cinfo);
fclose(outfile);
jpeg_destroy_compress(&cinfo);
}
/***************************************//**
*
* An implementation of jpeg source manager
*
***************************************/
static void
net_init_source (j_decompress_ptr cinfo)
{
/* no work here */
}
static boolean
net_fill_input_buffer (j_decompress_ptr cinfo)
{
struct jpeg_source_mgr *src = (struct jpeg_source_mgr*) cinfo->src;
printf("net_fill_input_buffer: should not reach here\n");
return TRUE;
}
static void
net_skip_input_data (j_decompress_ptr cinfo, long num_bytes)
{
/* no work here */
printf("Warning: net_skip_input_data() not implemented\n");
}
static void
net_term_source (j_decompress_ptr cinfo)
{
/* no work here */
}
/*
* Prepare for input from a stdio stream.
* The caller must have already opened the stream, and is responsible
* for closing it after finishing decompression.
*/
static void
net_jpeg_stdio_src (j_decompress_ptr cinfo, char* buf, int bufsize)
{
struct jpeg_source_mgr * src;
/* The source object and input buffer are made permanent so that a series
* of JPEG images can be read from the same file by calling jpeg_stdio_src
* only before the first one. (If we discarded the buffer at the end of
* one image, we'd likely lose the start of the next one.)
* This makes it unsafe to use this manager and a different source
* manager serially with the same JPEG object. Caveat programmer.
*/
if (cinfo->src == NULL) { /* first time for this JPEG object? */
cinfo->src = (struct jpeg_source_mgr *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
sizeof(struct jpeg_source_mgr));
src = cinfo->src;
}
src->init_source = net_init_source;
src->fill_input_buffer = net_fill_input_buffer;
src->skip_input_data = net_skip_input_data;
src->resync_to_restart = jpeg_resync_to_restart; /* use default method */
src->term_source = net_term_source;
src->bytes_in_buffer = bufsize;
src->next_input_byte = buf;
}
/*
* Sample routine for JPEG decompression. We assume that the source file name
* is passed in. We want to return 1 on success, 0 on error.
*/
int image_read_rgb_hsv_buf (const char *buf, int bufsize, int *width, int *height, unsigned char **data_rgb, unsigned char **data_hsv)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
unsigned char *orig;
unsigned char *rgb; /* Output row buffer */
unsigned char *hsv;
JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
int row_stride; /* physical row width in output buffer */
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
net_jpeg_stdio_src(&cinfo, buf, bufsize);
(void) jpeg_read_header(&cinfo, TRUE);
(void) jpeg_start_decompress(&cinfo);
row_stride = cinfo.output_width * cinfo.output_components;
orig = (unsigned char *)malloc(cinfo.output_width * cinfo.output_height * cinfo.output_components);
if (orig == NULL) fatal("out of memory");
row_pointer[0] = orig;
while (cinfo.output_scanline < cinfo.output_height) {
(void) jpeg_read_scanlines(&cinfo, row_pointer, 1);
row_pointer[0] += row_stride;
}
(void) jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
rgb = resize(orig, cinfo.output_width, cinfo.output_height, DEFAULT_SIZE, DEFAULT_SIZE);
hsv = (unsigned char *)malloc(DEFAULT_SIZE * DEFAULT_SIZE * CHAN);
rgb2hsv(rgb, DEFAULT_SIZE, DEFAULT_SIZE, hsv);
free(orig);
*width = DEFAULT_SIZE;
*height = DEFAULT_SIZE;
*data_rgb = rgb;
*data_hsv = hsv;
return 0;
}