Mercurial > hg > octave-image
changeset 158:b1ba33b88de2
Changes to handle MxNx3 images of different numeric classes:
* imread.m: Return the appropriate numeric class. Colour images are of size MxNx3, gray images MxN.
Uses ImageMagick's "identify" to find the image type (b&w/grayscale/colour).
* jpgread.cc: Error handler modified to prevent segfaults. Colour images are returned as MxNx3
matrices, gray images as MxN. Conversion from colour to grey-level is not done anymore (rather use
im2gray).
* pngread.cc: No segfault on loading invalid file types. Handle different depths correctly. Split
into pngread, pngwrite and pngcanvas. Return the appropriate numeric classes and matrix sizes.
* imread.m: Prefer jpgread and pngread to ImageMagick.
author | sjvdw |
---|---|
date | Sat, 10 Sep 2005 16:07:47 +0000 |
parents | 4aaa6584062c |
children | 3b34641f1dc9 |
files | Makefile imread.m jpgread.cc pngcanvas.h pngread.cc pngwrite.cc |
diffstat | 6 files changed, 638 insertions(+), 735 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile +++ b/Makefile @@ -22,6 +22,6 @@ $(MKOCTFILE) $< -lpng pngwrite.oct: - $(LN_S) pngread.oct $@ + ${MKOCTFILE) $< -lpng clean: ; -$(RM) *.o octave-core core *.oct *~
--- a/imread.m +++ b/imread.m @@ -5,392 +5,141 @@ ## the Free Software Foundation; either version 2, or (at your option) ## any later version. USE THIS SOFTWARE AT YOUR OWN RISK. -#IMREAD: read images into octave from various file formats -# -# Note: this requires the ImageMagick "convert" utility -# get this from www.imagemagick.org if required -# additional documentation of options is available from the -# convert man page -# -# BASIC USAGE: -# img = imread( fname ) -# - img is a greyscale (0-255) of image in fname -# [im,map]=imread( fname ) -# - map is a matrix of [r,g,b], 0-1 triples -# - img is a matrix on indeces into map -# [r,g,b]= imread( fname ) -# - r,g,b are red,green,blue (0-255) compondents -# -# Formats for image fname -# 1. simple guess from extention ie "fig.jpg", "blah.gif" -# 2. specify explicitly "jpg:fig.jpg", "gif:blah.gif" -# 3. specify subimage for multi-image format "tiff:file.tif[3]" -# 4. raw images (row major format) specify geometry -# "raw:img[256x180]" -# -# IMREAD OPTIONS: -# imread will support most of the options for convert.1 -# -# img = imread( fname , options) -# [r,g,b]= imread( fname , options) -# -# where options is a string matrix (or list) of options -# -# example: options= ["-rotate 25"; -# "-crop 200x200+150+150"; -# "-sample 200%" ]; -# will rotate, crop, and then expand the image. -# note that the order of operations is important -# -# The following options are supported -# -antialias remove pixel-aliasing -# -blur geometry blur the image -# -border geometry surround image with a border of color -# -bordercolor color border color -# -colors value maximum number of colors in the image -# -colorspace type alternate image colorspace -# -contrast enhance or reduce the image contrast -# -crop geometry preferred size and location of the cropped image -# -despeckle reduce the speckles within an image -# -dither apply Floyd/Steinberg error diffusion to image -# -draw string annotate the image with a graphic primitive -# -edge radius apply a filter to detect edges in the image -# -emboss radius emboss an image -# -enhance apply a digital filter to enhance a noisy image -# -equalize perform histogram equalization to an image -# -filter type use this filter when resizing an image -# -flip flip image in the vertical direction -# -flop flop image in the horizontal direction -# -font name font for rendering text -# -frame geometry surround image with an ornamental border -# -fuzz distance colors within this distance are considered equal -# -gamma value level of gamma correction -# -geometry geometry perferred size or location of the image -# -gaussian geometry gaussian blur an image -# -gravity type vertical and horizontal text placement -# -implode amount implode image pixels about the center -# -intent type Absolute, Perceptual, Relative, or Saturation -# -interlace type None, Line, Plane, or Partition -# -level value adjust the level of image contrast -# -map filename transform image colors to match this set of colors -# -median radius apply a median filter to the image -# -modulate value vary the brightness, saturation, and hue -# -monochrome transform image to black and white -# -mosaic create an mosaic from an image sequence -# -negate replace every pixel with its complementary color -# -noise radius add or reduce noise in an image -# -normalize transform image to span the full range of colors -# -paint radius simulate an oil painting -# -raise value lighten/darken image edges to create a 3-D effect -# -region geometry apply options to a portion of the image -# -roll geometry roll an image vertically or horizontally -# -rotate degrees apply Paeth rotation to the image -# -sample geometry scale image with pixel sampling -# -scale geometry resize image -# -scene value image scene number -# -segment values segment an image -# -seed value pseudo-random number generator seed value -# -shade degrees shade the image using a distant light source -# -sharpen geometry sharpen the image -# -shave geometry shave pixels from the image edges -# -shear geometry slide one edge of the image along the X or Y axis -# -size geometry width and height of image -# -solarize threshold negate all pixels above the threshold level -# -spread amount displace image pixels by a random amount -# -swirl degrees swirl image pixels about the center -# -texture filename name of texture to tile onto the image background -# -threshold value threshold the image -# -transparent color make this color transparent within the image -# -treedepth value depth of the color tree -# -type type image type -# -unsharp geometry sharpen the image -# -wave geometry alter an image along a sine wave +## usage: I = imread(fname) +## +## Read images from various file formats. +## +## The size and numeric class of the output depends on the +## format of the image. A colour image is returned as an +## MxNx3 matrix. Grey-level and black-and-white images are +## of size MxN. +## The colour depth of the image determines the numeric +## class of the output: 'uint8' or 'uint16' for grey +## and colour, and 'logical' for black and white. +## +## Note: For image formats other than jpeg and png, the +## ImageMagick "convert" and "identify" utilities +## are needed. ImageMagick can be found at +## +## www.imagemagick.org +## + +## Author: Andy Adler +## +## Modified: Stefan van der Walt <stefan@sun.ac.za> +## Date: 24 January 2005 + +function varargout = imread(filename, options) + if (nargin != 1) + usage("I = imread(filename)") + endif + + if !isstr(filename) + error("imread: filename must be a string") + endif -function [out1,out2,out3]= imread(filename, options ); - -try save_empty_list_elements_ok= empty_list_elements_ok; -catch save_empty_list_elements_ok= 0; -end -try save_warn_empty_list_elements= warn_empty_list_elements; -catch save_warn_empty_list_elements= 0; -end -unwind_protect -empty_list_elements_ok= 1; -warn_empty_list_elements= 0; - -# some older versions of octave didn't seem handle piped output correctly -usepipe=1; + fn = file_in_path(IMAGEPATH, filename); + if isempty(fn) + error("imread: cannot find %s", filename); + endif -if (nargin == 0) - usage (["img = imread (filename,options)\n", ... - "[r,g,b]= imread (filename,options)\n", ... - "[img,map]=imread (filename,options)" ]); -elseif (! ischar (filename)) - error ("imread: expecting filename as a string"); -endif - -if any(filename==':') || any(filename=='[') - # we've using special imagemagick characters, so we don't - # do any octave path processing - fname= filename; -else - fname= file_in_path(IMAGEPATH,filename); - if isempty(fname) - error(['imread: file ' filename ' not found']); - end -end + ## divert jpegs and pngs to "jpgread" and "pngread" + [ig, ig, ext] = fileparts(fn); + ext = upper(ext); + if ( file_in_loadpath("jpgread.oct") && + (strcmp(ext, ".JPG") || strcmp(ext, ".JPEG")) ) + varargout{1} = jpgread(fn); + break + endif + if ( file_in_loadpath("pngread.oct") && (strcmp(ext, ".PNG")) ) + varargout{1} = pngread(fn); + break + endif -# Put together the options string -# TODO: add some error checking here -if nargin==1 - option_str=""; -else - option_str=""; - if ( is_list( options ) ) - for i= 1:length(options) - option_str=[option_str," ", nth(options,i) ]; - end - elseif ( ischar( options ) ) - for i= 1:size(options,1) - option_str=[option_str," ", options(i,:) ]; - end - else - error ("imread: expecting options as a string"); - end -end -# -# decode the nargout to output what the user wants -# -if nargout==3; - wantgreyscale= 0; - wantmap= 0; - outputtype="ppm"; -elseif nargout==2; - wantgreyscale= 0; wantmap= 1; - outputtype="ppm"; -else - wantgreyscale= 1; wantmap= 0; - outputtype="pgm"; -end + [ident, sys] = system(sprintf("identify -verbose %s | grep -e Depth -e Type", + fn)); + if (sys != 0) + error("imread: error running ImageMagick's 'identify' on %s", fn) + endif + depth = re_grab("Depth: ([[:digit:]]{1,2})", ident); + imtype = re_grab("Type: ([[:alpha:]]*)", ident); -if usepipe - pname= sprintf("convert %s '%s' %s:- 2>/dev/null", - option_str, fname, outputtype); - fid= popen(pname ,'r'); -else - tnam= tmpnam(); - cmd= sprintf("convert %s '%s' '%s:%s' 2>/dev/null ", - option_str, fname, outputtype, tnam); - system(cmd); - fid= fopen(tnam,"rb"); -end + depth = str2num(depth); + if isempty(depth) || (pow2(nextpow2(depth)) != depth) + error("imread: invalid image depth %s", depth) + endif - -# -# can we open the pipe? -# if not 1) The file format is wrong and the conver program has bailed out -# 2) The apropriate converter program hasn't been installed -# - if fid<0; - if ~usepipe - unlink(tnam); - end - error('could not read image data. Is ImageMagick installed?'); - end + if !(strcmp(imtype, "Bilevel") || strcmp(imtype, "Grayscale") || + strcmp(imtype, "TrueColor")) + error("imread: unknown image type '%s'", imtype); + endif -# get file type - line= fgetl( fid ); - if strcmp(line, 'P1'); bpp=1; spp=1; bindata=0; - elseif strcmp(line, 'P4'); bpp=1; spp=1; bindata=1; - elseif strcmp(line, 'P2'); bpp=8; spp=1; bindata=0; - elseif strcmp(line, 'P5'); bpp=8; spp=1; bindata=1; - elseif strcmp(line, 'P3'); bpp=8; spp=3; bindata=0; - elseif strcmp(line, 'P6'); bpp=8; spp=3; bindata=1; - else - if usepipe - pclose(fid); - else - fclose(fid); unlink(tnam); - end - error(['Image format error for ',fname,':line=', char(line)]); - end + switch (imtype) + case("Bilevel") + fmt = "pgm"; + case("Grayscale") + fmt = "pgm"; + case("TrueColor") + fmt = "ppm"; + endswitch + + ## Why are pipes so slow? + ## cmd = sprintf("convert -flatten -strip %s %s:-", fn, fmt); -# ignore comments - line= fgetl( fid ); - while length(line)==0 || line(1)=='#' - line= fgetl( fid ); - end - -# get width, height - [wid, hig]= sscanf( line, '%d %d', 'C' ); - -# get max component value - if bpp==8 - max_component= sscanf( fgetl(fid), '%d' ); - if (max_component > 255) - # The PGM standard supports only values below 65536 - bpp = 16; - endif - end - - if bindata - if(bpp == 16) - # PGM format has MSB first, i.e. big endian - data = fread(fid, "uint16", 0, "ieee-be"); - else - data = fread(fid); - endif - numdata= size(data,1); + tmpf = tmpnam(); + cmd = sprintf("convert -flatten -strip '%s' '%s:%s' 2>/dev/null", + fn, fmt, tmpf); - if bpp==1 - data= rem( floor( (data*ones(1,8)) ./ ... - (ones(length(data),1)*[128 64 32 16 8 4 2 1]) ), 2)'; - end - else - numdata= wid*hig*spp; - data= zeros( numdata,1 ); - dptr= 1; - - line= fgetl( fid ); - while !feof( fid) - rdata= sscanf( line ,' %d'); - nptr= dptr + size(rdata,1); - data( dptr:nptr-1 ) = rdata; - dptr= nptr; - line= fgetl( fid ); - end # feof - end #if bindata + [ignored, sys] = system(cmd); + + if (sys != 0) + error("imread: error running ImageMagick's 'convert'"); + unlink(tmpf); + endif + + try + ##fid = popen(cmd, "r"); + fid = fopen(tmpf, "rb"); + catch + unlink(tmpf); + error("imread: could not open temporary file %s", tmpf) + end_try_catch - if usepipe - pclose(fid); - else - fclose(fid); unlink(tnam); - end - - if spp==1 - greyimg= reshape( data(:), wid, hig )'; - elseif spp==3 - redimg= reshape( data(1:spp:numdata), wid, hig )'; - grnimg= reshape( data(2:spp:numdata), wid, hig )'; - bluimg= reshape( data(3:spp:numdata), wid, hig )'; - else - error(sprintf("imread: don't know how to handle pnm with spp=%d",spp)); - end - - -# This section outputs the image in the desired output format -# if the user requested the colour map, the we regenerate it from -# the image. -# -# Of course, 1) This may result in huge colourmaps -# 2) The colourmap will not be in the same order as -# in the original file - -if wantgreyscale + fgetl(fid); # P5 or P6 (pgm or ppm) + [width, height] = sscanf(fgetl(fid), "%d %d", "C"); + fgetl(fid); # ignore max components - if exist('greyimg') - out1= greyimg; - elseif exist('idximg') - greymap= mean(map')'; - out1= reshape( greymap( idximg ) , size(idximg,1), size(idximg,2) ); - else - out1= ( redimg+grnimg+bluimg ) / 3 ; - end - -elseif wantmap - - if exist('idximg') - out1= idximg; - out2= map; - elseif exist('greyimg') - [simg, sidx] = sort( greyimg(:) ); - [jnkval, sidx] = sort( sidx ); - - dimg= [1; diff(simg)>0 ]; - cimg= cumsum( dimg ); - out1= reshape( cimg( sidx ) , hig, wid); - out2= ( simg(find( dimg ))*[1,1,1] - 1)/256; - else -# -# Generate a colourmap for RGB images by packing RGB into -# an integer and assigning the unique values to a colormap. -# - c_range= 256; # use 65536 for 48 bit color - [simg, sidx] = sort( round(redimg(:)) + ... - c_range *round(grnimg(:)) + ... - c_range^2 *round(bluimg(:)) ); - [jnkval, sidx] = sort( sidx ); - - dimg= [1; diff(simg)>0 ]; - cimg= cumsum( dimg ); - out1= reshape( cimg( sidx ) , hig, wid ); - tmpv= simg(find( dimg )); - out2= [ rem(tmpv,c_range), ... - rem(floor(tmpv/c_range), c_range), ... - floor(tmpv/c_range^2) ]/c_range; - end - - -else + if(depth == 16) + ## PGM format has MSB first, i.e. big endian + data = fread(fid, "uint16", 0, "ieee-be"); + else + data = fread(fid); + endif + + fclose(fid); + unlink(tmpnam); - if exist('greyimg') - out1= greyimg; - out2= greyimg; - out3= greyimg; - else - out1= redimg; - out2= grnimg; - out3= bluimg; - end - -end - -unwind_protect_cleanup -empty_list_elements_ok= save_empty_list_elements_ok; -warn_empty_list_elements= save_warn_empty_list_elements; -end_unwind_protect + varargout = {}; + switch (imtype) + case ("Bilevel") + varargout{1} = logical(reshape(data, height, width)'); + case ("Grayscale") + varargout{1} = uint8(reshape(data, height, width)'); + case ("TrueColor") + varargout{1} = cat(3, reshape(data(1:3:end), height, width)', + reshape(data(2:3:end), height, width)', + reshape(data(3:3:end), height, width)'); + eval(sprintf("varargout{1} = uint%d(varargout{1});", depth)); + + endswitch +endfunction -# -# $Log$ -# Revision 1.14 2005/09/08 03:12:29 pkienzle -# [for Bill Denney] setstr -> char -# -# Revision 1.13 2005/09/08 02:00:17 pkienzle -# [for Bill Denney] isstr -> ischar -# -# Revision 1.12 2005/07/21 16:03:01 aadler -# Improve error messages and use pipes -# -# Revision 1.11 2005/05/25 03:43:40 pkienzle -# Author/Copyright consistency -# -# Revision 1.10 2005/04/25 01:05:28 aadler -# added GPL copyrights -# -# Revision 1.9 2004/02/18 14:54:10 pkienzle -# Colour values from ppm are 0-255 not 1-256 -# -# Revision 1.8 2003/11/14 16:55:10 tpikonen -# Fix endianess bug in 16-bit reads. -# -# Revision 1.6 2003/09/12 14:22:42 adb014 -# Changes to allow use with latest CVS of octave (do_fortran_indexing, etc) -# -# Revision 1.5 2003/07/25 19:11:41 pkienzle -# Make sure all files names referenced in system calls are wrapped in quotes -# to protect against spaces in the path. -# -# Revision 1.4 2002/11/27 08:40:11 pkienzle -# author/license updates -# -# Revision 1.3 2002/03/19 18:14:13 aadler -# unfortunately using popen seems to create problems, mostly -# on win32, but also on linux, so we need to move to a tmpfile approach -# -# Revision 1.2 2002/03/17 05:26:14 aadler -# now accept filenames with spaces -# -# Revision 1.1 2002/03/11 01:56:47 aadler -# general image read/write functionality using imagemagick utilities -# -# +function value = re_grab(re, str) + idx = regexp(re, str); + if !isempty(idx) + idx = idx(2,:); + value = substr(str, idx(1), diff(idx)+1); + else + value = ""; + endif +endfunction
--- a/jpgread.cc +++ b/jpgread.cc @@ -9,32 +9,39 @@ // $Id$ // -#include <octave/oct.h> -#include <iostream> +/* Modified: Stefan van der Walt <stefan@sun.ac.za> + * Date: 27 January 2004 + * - Manual error handler to prevent segfaults in Octave. + * - Use uint8NDArray for output. + */ /* - * Simple jpeg reading MEX-file. - * * Compilation: * First, try * mkoctfile jpgread.cc -ljpeg * - * If this doesn't work, then do - * - * Calls the jpeg library which is part of + * If this doesn't work, install the jpeg library which is part of * "The Independent JPEG Group's JPEG software" collection. * - * The jpeg library came from, + * The jpeg library came from * * ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6.tar.gz + * + * Extract and build the library: * tar xvfz jpegsrc.v6.tar.gz * cd jpeg-6b * ./configure * make * make test - * mkoctfile jpgread.cc -I<jpeg-6b include> -L<jpeg-6b lib> -ljpeg + * + * Compile this file using: + * mkoctfile jpgread.cc -I<jpeg-6b include dir> -L<jpeg-6b lib dir> -ljpeg */ +#include <octave/oct.h> +#include <iostream> +#include <csetjmp> + #ifdef __cplusplus extern "C" { #endif @@ -45,137 +52,130 @@ } //extern "C" #endif -DEFUN_DLD (jpgread, args, nargout , -"JPGREAD Read a JPEG file from disk. \n\ - [R,G,B] = jpgread('filename') reads the specified file \n\ - and returns the Red, Green, and Blue intensity matrices.\n\ -\n\ - [X] = jpgread('filename') reads the specified file \n\ - and returns the average intensity matrix.") -{ - octave_value_list retval; - int nargin = args.length(); +struct oct_error_mgr { + struct jpeg_error_mgr pub; /* "public" fields */ + jmp_buf setjmp_buffer; /* for return to caller */ +}; - FILE * infile; +typedef struct oct_error_mgr * oct_error_ptr; - JSAMPARRAY buffer; - long row_stride; - struct jpeg_decompress_struct cinfo; - struct jpeg_error_mgr jerr; - -// -// We bail out if the input parameters are bad -// - if (nargin != 1 || !args(0).is_string() ) { - print_usage ("jpgread"); - return retval; - } - - if (nargout != 1 && nargout != 3 ) { - print_usage ("jpgread"); - return retval; - } +METHODDEF(void) +oct_error_exit (j_common_ptr cinfo) +{ + /* cinfo->err really points to an oct_error_mgr struct, so coerce pointer */ + oct_error_ptr octerr = (oct_error_ptr) cinfo->err; + + /* Format error message and send to interpreter */ + char errmsg[JMSG_LENGTH_MAX]; + (octerr->pub.format_message)(cinfo, errmsg); + error("jpgread: %s", errmsg); + + /* Return control to the setjmp point */ + longjmp(octerr->setjmp_buffer, 1); +} -// -// Open jpg file -// - std::string filename = args(0).string_value(); - if ((infile = fopen(filename.c_str(), "rb")) == NULL) { - error("Couldn't open file"); - return retval; - } - -// -// Initialize the jpeg library -// - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_decompress(&cinfo); - -// -// Read the jpg header to get info about size and color depth -// - jpeg_stdio_src(&cinfo, infile); - jpeg_read_header(&cinfo, TRUE); - jpeg_start_decompress(&cinfo); - /* - if (cinfo.output_components == 1) { // Grayscale - jpeg_destroy_decompress(&cinfo); - error("Grayscale jpegs not supported"); - } - */ - -// -// Allocate buffer for one scan line -// - - row_stride = cinfo.output_width * cinfo.output_components; - buffer = (*cinfo.mem->alloc_sarray) - ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); +DEFUN_DLD (jpgread, args, nargout , +"usage: I = jpgread('filename')\n\ +\n\ + Read a JPEG file from disk.\n\ +\n\ + For a grey-level image, the output is an MxN matrix. For a\n\ + colour image, three such matrices are returned (MxNx3),\n\ + representing the red, green and blue components. The output\n\ + is of class 'uint8'.\n\ +\n\ + See also: imread, im2double, im2gray, im2rgb.") +{ + octave_value_list retval; + int nargin = args.length(); + + FILE * infile; + + JSAMPARRAY buffer; + long row_stride; + struct jpeg_decompress_struct cinfo; + struct oct_error_mgr jerr; -// -// Create 3 matrices, One each for the Red, Green, and Blue component of the image. -// or create 1 matrix for the avg intensity -// -// Now, loop through each of the scanlines. For each, copy the image -// data from the buffer, convert to double. -// - - if (nargout == 3) { - Matrix red ( cinfo.output_height , cinfo.output_width ); - Matrix green ( cinfo.output_height , cinfo.output_width ); - Matrix blue ( cinfo.output_height , cinfo.output_width ); - - for (unsigned long j=0; cinfo.output_scanline < cinfo.output_height; j++) { - jpeg_read_scanlines(&cinfo, buffer,1); - - if (cinfo.output_components == 1) { // Grayscale - for (unsigned long i=0; i<cinfo.output_width; i++) { - red(j,i) = - green(j,i) = - blue(j,i) = (double) buffer[0][i*3+2]; - } // for i - } else { // not Grayscale - for (unsigned long i=0; i<cinfo.output_width; i++) { - red(j,i) = (double) buffer[0][i*3+0]; - green(j,i) = (double) buffer[0][i*3+1]; - blue(j,i) = (double) buffer[0][i*3+2]; - } // for i - } // not grayscale - } //for j - - retval(0)= red; - retval(1)= green; - retval(2)= blue; - - } // if nargout==3 - else { - Matrix avg ( cinfo.output_height , cinfo.output_width ); - - for (unsigned long j=0; cinfo.output_scanline < cinfo.output_height; j++) { - jpeg_read_scanlines(&cinfo, buffer,1); - - if (cinfo.output_components == 1) { // Grayscale - for (unsigned long i=0; i<cinfo.output_width; i++) { - avg(j,i) = (double) buffer[0][i]; - } // for i - } else { // not Grayscale - for (unsigned long i=0; i<cinfo.output_width; i++) { - avg(j,i) = ((double) buffer[0][i*3+0] + - buffer[0][i*3+1] + - buffer[0][i*3+2] ) / 3; - } // for i - } // not Grayscale - } //for j - - retval(0)= avg; - } // if nargout - -// -// Clean up -// - jpeg_finish_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); - fclose(infile); - - return retval; -}; + // + // We bail out if the input parameters are bad + // + if ((nargin != 1) || !args(0).is_string() || (nargout != 1)) { + print_usage ("jpgread"); + return retval; + } + + // + // Open jpg file + // + std::string filename = args(0).string_value(); + if ((infile = fopen(filename.c_str(), "rb")) == NULL) { + error("jpgread: couldn't open file %s", filename.c_str()); + return retval; + } + + // + // Initialize the jpeg library + // + cinfo.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = oct_error_exit; + if (setjmp(jerr.setjmp_buffer)) { + /* If we get here, the JPEG code has signaled an error. + * We need to clean up the JPEG object, close the input file, and return. + */ + jpeg_destroy_decompress(&cinfo); + fclose(infile); + return retval; + } + + jpeg_create_decompress(&cinfo); + + // + // Read the jpg header to get info about size and color depth + // + jpeg_stdio_src(&cinfo, infile); + jpeg_read_header(&cinfo, TRUE); + jpeg_start_decompress(&cinfo); + + // + // Allocate buffer for one scan line + // + row_stride = cinfo.output_width * cinfo.output_components; + buffer = (*cinfo.mem->alloc_sarray) + ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); + + // + // Create an NDArray for the output. Loop through each of the + // scanlines and copy the image data from the buffer. + // + + dim_vector dim = dim_vector(); + dim.resize(3); + dim(0) = cinfo.output_height; + dim(1) = cinfo.output_width; + dim(2) = cinfo.output_components; + uint8NDArray out = uint8NDArray(dim, 0); + + Array<int> coord = Array<int> (3); + for (unsigned long j=0; cinfo.output_scanline < cinfo.output_height; j++) { + jpeg_read_scanlines(&cinfo, buffer, 1); + + coord(0) = j; + for (unsigned long i=0; i<cinfo.output_width; i++) { + coord(1) = i; + for (unsigned int c = 0; c < cinfo.output_components; c++) { + coord(2) = c; + out(coord) = buffer[0][i*cinfo.output_components+c]; + } + } + } + retval.append(out.squeeze()); + + // + // Clean up + // + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + fclose(infile); + + return retval; +}
new file mode 100644 --- /dev/null +++ b/pngcanvas.h @@ -0,0 +1,76 @@ +/* + * readpng.h + * + * Copyright (C) 2003 Nadav Rotem <nadav256@hotmail.com> + * + * This program 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 of the License, 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Modified: Stefan van der Walt <stefan@sun.ac.za> + * Date: 28 January 2005 + * - Fix bugs, restructure + */ + +typedef struct +{ + int width; + int height; + int bit_depth; + int color_type; + unsigned char **row_pointers; +} canvas; + +//////////////Libcanvas/////////// +canvas *new_canvas(int width, int height, int stride) +{ + // Default stride if none given + if (stride==0) stride=width*4; + + // Clean allocation of canvas structure + canvas *can=new(canvas); + unsigned char *image_data = new unsigned char[stride*height]; + unsigned char **row_pointers = new unsigned char *[height]; + if (can == NULL || image_data == NULL || row_pointers == NULL) + { + if (can == NULL) delete can; + if (image_data == NULL) delete[] image_data; + if (row_pointers == NULL) delete[] row_pointers; + return NULL; + } + + // Fill in canvas structure + can->width=width; + can->height=height; + can->bit_depth=8; + can->color_type=PNG_COLOR_TYPE_RGB_ALPHA; + can->row_pointers = row_pointers; + for (int i=0; i < height; i++) row_pointers[i] = image_data + i*stride; + + return can; +} + +void delete_canvas(canvas *can) +{ + + if (can!=NULL) + { + delete[] can->row_pointers[0]; + delete[] can->row_pointers; + delete can; + } + return; +} +
--- a/pngread.cc +++ b/pngread.cc @@ -1,5 +1,5 @@ /* - * readpng.c + * pngread.cc * * Copyright (C) 2003 Nadav Rotem <nadav256@hotmail.com> * @@ -18,10 +18,9 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - /* -Loads PNG files to octave using libpng; +Load PNG files to octave using libpng; PNG (Portable Network Graphics) is an extensible file format for the lossless, portable, well-compressed storage of raster images. PNG pro- @@ -32,23 +31,27 @@ */ -#include <png.h> +/* + * Modified: Stefan van der Walt <stefan@sun.ac.za> + * Date: 28 January 2005 + * - Fix bugs, restructure + */ + #include <octave/oct.h> -typedef struct -{ - int width; - int height; - int bit_depth; - int color_type; - unsigned char **row_pointers; -} canvas; +#ifdef __cplusplus +extern "C" { +#endif + +#include "png.h" + +#ifdef __cplusplus +} //extern "C" +#endif + +#include "pngcanvas.h" canvas *load_canvas(char *filename); -void save_canvas(canvas *can,char *filename); -canvas *new_canvas(int x, int y, int stride=0); -void delete_canvas(canvas *can); - DEFUN_DLD (pngread, args, nargout ,"\ pngread reads a png file from disk.\n\ @@ -61,7 +64,7 @@ // // We bail out if the input parameters are bad // - if (nargin != 1 || !args(0).is_string() || nargout < 3) { + if (nargin != 1 || !args(0).is_string()) { print_usage ("pngread"); return retval; } @@ -70,145 +73,60 @@ // Load png file // canvas *pic=load_canvas((char *)args(0).string_value().c_str()); - - // octave_stdout << "Canvas [" << pic->width << "x" << pic->height << "]\n"; - - if (nargout >= 3) { - Matrix red ( pic->height , pic->width ); - Matrix green ( pic->height , pic->width ); - Matrix blue ( pic->height , pic->width ); - Matrix alpha ( pic->height , pic->width ); - - for (unsigned long j=0; j<pic->height; j++) - for (unsigned long i=0; i<pic->width; i++) - { - red(j,i) = pic->row_pointers[j][i*4+0]; - green(j,i) = pic->row_pointers[j][i*4+1] ; - blue(j,i) = pic->row_pointers[j][i*4+2]; - alpha(j,i) = pic->row_pointers[j][i*4+3]; - } - retval(3)= alpha; - retval(2)= blue; - retval(1)= green; - retval(0)= red; - } + if (!pic) return retval; + + dim_vector dim = dim_vector(); + dim.resize(3); + dim(0) = pic->height; + dim(1) = pic->width; + dim(2) = 3; - return retval; -} - + if ( (pic->color_type == PNG_COLOR_TYPE_GRAY) || + (pic->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) || + ((pic->color_type == PNG_COLOR_TYPE_PALETTE) && (pic->bit_depth == 1)) ) + dim(2) = 1; -DEFUN_DLD (pngwrite, args, ,"\ -pngwrite writes a png file to the disk.\n\ - pngwrite('filename',R,G,B,A) writes the specified file\n\ - using the Red, Green, Blue and Alpha matrices.\n\ - \n\ - Data must be [0 255] or the high bytes will be lost.") -{ - octave_value_list retval; - int nargin = args.length(); - - // - // We bail out if the input parameters are bad - // - if (nargin < 4 || !args(0).is_string() ) { - print_usage ("pngwrite"); - return retval; - } + if (pic->bit_depth > 1 && pic->bit_depth < 8) + pic->bit_depth = 8; - Matrix red = args(1).matrix_value(); - Matrix green= args(2).matrix_value(); - Matrix blue = args(3).matrix_value(); - Matrix alpha= args(4).matrix_value(); - - long image_width = args(1).columns(); - long image_height = args(1).rows(); - - if ( args(2).columns() != image_width || - args(3).columns() != image_width || - args(4).columns() != image_width || - args(2).rows() != image_height || - args(3).rows() != image_height || - args(4).rows() != image_height ) - { - error("pngwrite R,G,B,A matrix sizes aren't the same"); - return retval; - } - - canvas *pic=new_canvas(image_width,image_height); - if (pic == NULL) - { - error("pngwrite out of memory"); - return retval; - } - - // octave_stdout << "Canvas [" << pic->width << "x" << pic->height << "]\n"; - - for(int i=0; i<pic->width; i++) - for(int j=0; j<pic->height; j++) - { - pic->row_pointers[j][i*4+0]=(unsigned char)(red(j,i)); - pic->row_pointers[j][i*4+1]=(unsigned char)(green(j,i)); - pic->row_pointers[j][i*4+2]=(unsigned char)(blue(j,i)); - pic->row_pointers[j][i*4+3]=(unsigned char)(alpha(j,i)); - } - - save_canvas(pic,(char *)args(0).string_value().c_str()); - delete_canvas(pic); + NDArray out = NDArray(dim, 0); + + Array<int> coord = Array<int> (3); + Matrix alpha ( pic->height , pic->width ); + + for (unsigned long j=0; j < pic->height; j++) { + coord(0) = j; + for (unsigned long i=0; i < pic->width; i++) { + coord(1) = i; - return retval; -} - -//////////////Libcanvas/////////// -canvas *new_canvas(int width, int height, int stride) -{ - // Default stride if none given - if (stride==0) stride=width*4; - - // Clean allocation of canvas structure - canvas *can=new(canvas); - unsigned char *image_data = new unsigned char[stride*height]; - unsigned char **row_pointers = new unsigned char *[height]; - if (can == NULL || image_data == NULL || row_pointers == NULL) - { - if (can == NULL) delete can; - if (image_data == NULL) delete[] image_data; - if (row_pointers == NULL) delete[] row_pointers; - return NULL; - } + for (int c = 0; c < dim(2); c++) { + coord(2) = c; + out(coord) = pic->row_pointers[j][i*3+c]; + } + alpha(j,i) = pic->row_pointers[j][i*3+3]; + } + } + out = out.squeeze(); - // Fill in canvas structure - can->width=width; - can->height=height; - can->bit_depth=8; - can->color_type=PNG_COLOR_TYPE_RGB_ALPHA; - can->row_pointers = row_pointers; - for (int i=0; i < height; i++) row_pointers[i] = image_data + i*stride; - - return can; -} - + switch (pic->bit_depth) { + case 1: retval.append((boolNDArray)out); break; + case 8: retval.append((uint8NDArray)out); break; + case 16: retval.append((uint16NDArray)out); break; + default: retval.append(out); + } + retval.append(alpha); - -void delete_canvas(canvas *can) -{ - - if (can!=NULL) - { - delete[] can->row_pointers[0]; - delete[] can->row_pointers; - delete can; - } - return; + delete_canvas(pic); + return retval; } canvas *load_canvas(char *filename) { - FILE *infile; png_structp png_ptr; png_infop info_ptr; - infile = fopen(filename,"r"); - if (NULL==infile) { + FILE *infile = fopen(filename,"r"); + if (!infile) { error("pngread could not open file %s", filename); return NULL; } @@ -221,9 +139,9 @@ return NULL; } - png_ptr=png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); if (!png_ptr) { - error("pngread out of mem"); + error("pngread out of memory"); fclose(infile); return NULL; } @@ -235,96 +153,77 @@ fclose(infile); return NULL; } - - // XXX FIXME XXX do we need to check for failure in any of these steps? - png_init_io(png_ptr,infile); - png_set_sig_bytes(png_ptr,8); - png_read_info(png_ptr,info_ptr); + + /* Set error handling */ + if (setjmp(png_jmpbuf(png_ptr))) { + error("pngread: libpng exited abnormally"); + png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL); + fclose(infile); + return NULL; + } + + png_init_io(png_ptr, infile); + png_set_sig_bytes(png_ptr, 8); + png_read_info(png_ptr, info_ptr); png_uint_32 width,height; - int color_type,bit_depth; + int color_type, bit_depth; png_get_IHDR(png_ptr,info_ptr,&width,&height, - &color_type,&bit_depth,NULL,NULL,NULL); - - // Set to RGB - ALPHA - 8bit - if (color_type == PNG_COLOR_TYPE_PALETTE) - png_set_palette_to_rgb(png_ptr); + &bit_depth,&color_type,NULL,NULL,NULL); - if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) - png_set_gray_1_2_4_to_8(png_ptr); + /* Transform grayscale images with depths < 8-bit to 8-bit, change + * paletted images to RGB, and add a full alpha channel if there is + * transparency information in a tRNS chunk. + */ + if (color_type == PNG_COLOR_TYPE_PALETTE) { + png_set_palette_to_rgb(png_ptr); + } + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { + png_set_gray_1_2_4_to_8(png_ptr); + } + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { //add alpha + png_set_tRNS_to_alpha(png_ptr); + } - if (png_get_valid(png_ptr, info_ptr,PNG_INFO_tRNS)) //add alpha - png_set_tRNS_to_alpha(png_ptr); - - if (bit_depth == 16) - png_set_strip_16(png_ptr); - + // Assume black background + if (color_type & PNG_COLOR_MASK_ALPHA) { + png_set_strip_alpha(png_ptr); + } + + // Always transform image to RGB if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - png_set_gray_to_rgb(png_ptr); + png_set_gray_to_rgb(png_ptr); + + if (bit_depth < 8) { + png_set_packing(png_ptr); + } - if (color_type ==2) - png_set_filler(png_ptr,0xff, PNG_FILLER_AFTER); - + // For now, use 8-bit only + if (bit_depth == 16) { + png_set_strip_16(png_ptr); + } + png_read_update_info(png_ptr,info_ptr); + // Read the data from the file - int stride=png_get_rowbytes(png_ptr,info_ptr); - canvas *can = new_canvas(width,height,stride); - if (can != NULL) { - png_read_image(png_ptr,can->row_pointers); + int stride = png_get_rowbytes(png_ptr, info_ptr); + canvas *can = new_canvas(width, height, stride); + + if (can) { + png_read_image(png_ptr, can->row_pointers); } else { - error("pngread out of mem"); + error("pngread out of memory"); } png_read_end(png_ptr,NULL); png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); fclose(infile); - + + // Set color type and depth. Used to determine octave output arguments. + can->color_type = color_type; + can->bit_depth = bit_depth; return can; } - -void save_canvas(canvas *can,char *filename) -{ - FILE *fp; - png_structp png_ptr; - png_infop info_ptr; - - fp = fopen(filename, "wb"); - if (fp == NULL) { - error("pngwrite could not open %s", filename); - return; - } - - // XXX FIXME XXX do we need to check for failure in any of these steps? - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - info_ptr = png_create_info_struct(png_ptr); - png_init_io(png_ptr, fp); - png_set_compression_level(png_ptr, 3); - - png_set_IHDR(png_ptr, info_ptr, can->width, can->height, - can->bit_depth, can->color_type, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - - - png_set_gAMA(png_ptr, info_ptr, 0.7); - - time_t gmt; - png_time mod_time; - png_text text_ptr[2]; - time(&gmt); - png_convert_from_time_t(&mod_time, gmt); - png_set_tIME(png_ptr, info_ptr, &mod_time); - text_ptr[0].key = "Created by"; - text_ptr[0].text = "Octave"; - text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE; - - png_set_text(png_ptr, info_ptr, text_ptr, 1); - - png_write_info(png_ptr, info_ptr); - png_write_image(png_ptr, can->row_pointers); - png_write_end(png_ptr, info_ptr); - png_destroy_write_struct(&png_ptr, &info_ptr); - fclose(fp); -}
new file mode 100644 --- /dev/null +++ b/pngwrite.cc @@ -0,0 +1,179 @@ +/* + * pngwrite.cc + * + * Copyright (C) 2003 Nadav Rotem <nadav256@hotmail.com> + * + * This program 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 of the License, 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + +Write PNG files to disk from octave + + PNG (Portable Network Graphics) is an extensible file format for the + lossless, portable, well-compressed storage of raster images. PNG pro- + vides a patent-free replacement for GIF and can also replace many com- + mon uses of TIFF. Indexed-color, grayscale, and truecolor images are + supported, plus an optional alpha channel. Sample depths range from 1 + to 16 bits. + +*/ + +/* + * Modified: Stefan van der Walt <stefan@sun.ac.za> + * Date: 28 January 2005 + * - Fix bugs, restructure + */ + +#include <octave/oct.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include "png.h" + +#ifdef __cplusplus +} //extern "C" +#endif + +#include "pngcanvas.h" + +void save_canvas(canvas *can, char *filename); + +DEFUN_DLD (pngwrite, args, ,"\ +pngwrite writes a png file to the disk.\n\ + pngwrite('filename',R,G,B,A) writes the specified file\n\ + using the Red, Green, Blue and Alpha matrices.\n\ + \n\ + Data must be [0 255] or the high bytes will be lost.") +{ + octave_value_list retval; + int nargin = args.length(); + + // + // We bail out if the input parameters are bad + // + if (nargin < 5 || !args(0).is_string() ) { + print_usage ("pngwrite"); + return retval; + } + + Matrix red = args(1).matrix_value(); + Matrix green= args(2).matrix_value(); + Matrix blue = args(3).matrix_value(); + Matrix alpha= args(4).matrix_value(); + + long image_width = args(1).columns(); + long image_height = args(1).rows(); + + if ( args(2).columns() != image_width || + args(3).columns() != image_width || + args(4).columns() != image_width || + args(2).rows() != image_height || + args(3).rows() != image_height || + args(4).rows() != image_height ) + { + error("pngwrite R,G,B,A matrix sizes aren't the same"); + return retval; + } + + octave_stdout << "we reached point A" << std::endl; + canvas *pic=new_canvas(image_width, image_height, image_width*4); + if (!pic) { + error("pngwrite out of memory"); + return retval; + } + + octave_stdout << "we reached point B" << std::endl; + for(int i=0; i < pic->width; i++) { + for(int j=0; j < pic->height; j++) { + pic->row_pointers[j][i*4+0]=(unsigned char)(red(j,i)); + pic->row_pointers[j][i*4+1]=(unsigned char)(green(j,i)); + pic->row_pointers[j][i*4+2]=(unsigned char)(blue(j,i)); + pic->row_pointers[j][i*4+3]=(unsigned char)(alpha(j,i)); + } + } + + octave_stdout << "we reached point C" << std::endl; + + save_canvas(pic,(char *)args(0).string_value().c_str()); + delete_canvas(pic); + + return retval; +} + +void save_canvas(canvas *can,char *filename) +{ + FILE *fp; + png_structp png_ptr; + png_infop info_ptr; + + fp = fopen(filename, "wb"); + if (fp == NULL) { + error("pngwrite could not open %s", filename); + return; + } + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + fclose(fp); + error("pngwrite: cannot create write structure"); + return; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + fclose(fp); + error("pngwrite: cannot not create image structure"); + png_destroy_write_struct(&png_ptr, png_infopp_NULL); + return; + } + + if (setjmp(png_jmpbuf(png_ptr))) { + fclose(fp); + png_destroy_write_struct(&png_ptr, &info_ptr); + error("pngread: libpng exited abnormally"); + return; + } + + png_init_io(png_ptr, fp); + png_set_compression_level(png_ptr, 3); + + png_set_IHDR(png_ptr, info_ptr, can->width, can->height, + can->bit_depth, can->color_type, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + + png_set_gAMA(png_ptr, info_ptr, 0.7); + + time_t gmt; + png_time mod_time; + png_text text_ptr[2]; + time(&gmt); + png_convert_from_time_t(&mod_time, gmt); + png_set_tIME(png_ptr, info_ptr, &mod_time); + text_ptr[0].key = "Created by"; + text_ptr[0].text = "Octave"; + text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE; + + png_set_text(png_ptr, info_ptr, text_ptr, 1); + + png_write_info(png_ptr, info_ptr); + png_write_image(png_ptr, can->row_pointers); + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fp); +}