Skip to content

Commit

Permalink
Reading and writing PNG files.
Browse files Browse the repository at this point in the history
  • Loading branch information
crisluengo committed Mar 11, 2024
1 parent 2f8ba87 commit dae9724
Show file tree
Hide file tree
Showing 6 changed files with 558 additions and 3 deletions.
70 changes: 69 additions & 1 deletion include/diplib/file_io.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* (c)2017-2021, Cris Luengo.
* (c)2017-2024, Cris Luengo.
* Based on original DIPlib code: (c)1995-2014, Delft University of Technology.
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -381,6 +381,74 @@ DIP_EXPORT void ImageWriteJPEG(
);


/// \brief Reads an image from the PNG file `filename` and puts it in `out`.
///
/// The function tries to open `filename` as given first, and if that fails, it appends ".png" to the
/// name and tries again.
///
/// PNG images are either gray-scale (scalar) or sRGB images, the color space information will be set accordingly.
/// If the image has an alpha channel, it will be the second or fourth tensor element in `out`.
///
/// The pixel size information, if present in the PNG file, will be used to set the pixel size of `out`.
DIP_EXPORT FileInformation ImageReadPNG(
Image& out,
String const& filename
);
DIP_NODISCARD inline Image ImageReadPNG(
String const& filename
) {
Image out;
ImageReadPNG( out, filename );
return out;
}

/// \brief Reads image information and metadata from the PNG file `filename`, without reading the actual
/// pixel data.
DIP_EXPORT FileInformation ImageReadPNGInfo( String const& filename );

/// \brief Returns true if the file `filename` is a PNG file.
DIP_EXPORT bool ImageIsPNG( String const& filename );

/// \brief Writes `image` as a PNG file.
///
/// `image` must be 2D, and either scalar or with two, three or four tensor elements.
/// If the image has three or four tensor elements, it will be saved as an sRGB image, even if the color space
/// is not sRGB (no color space conversion is done, the data is simply tagged as sRGB). If the image has two or
/// four tensor elements, the last tensor element is assumed to be the alpha channel.
/// If the image is not \ref dip::DT_UINT8, it will be converted to it (complex numbers are cast to real values
/// by taking their magnitude, and real numbers are rounded and clamped to the output range), no scaling will
/// be applied. Except if the image is \ref dip::DT_UINT16, which is accepted by the PNG standard and will be
/// written to file as-is.
///
/// If `filename` does not have an extension, ".png" will be added. Overwrites any other file with the same name.
///
/// `compressionLevel` sets the compression level; it's an integer in the range 0-9, with 0 for no compression,
/// 1 for the fastest method producing the largest output files, and 9 the slowest method producing the smallest
/// output files. The default is 6.
///
/// `filterChoice` specifies how the PNG file is filtered during compression. The compression algorithm will try
/// all selected filters on each line, and pick the best one. The set can contain one or several of these strings:
///
/// - "disable": No filtering, cannot be combined with the other methods.
/// - "none": No filtering.
/// - "sub": Each byte is replaced with the difference between it and the byte to its left.
/// - "up": Each byte is replaced with the difference between it and the corresponding byte on the previous image line.
/// - "avg": Each byte is replaced with the difference between it and the average of the corresponding bytes to its left and above it, truncating any fractional part.
/// - "Paeth": Each byte is replaced with the difference between it and the Paeth predictor of the corresponding bytes to its left, above it, and to its upper left.
/// - "all": Shortcut for including all five filters. This is the default. Cannot be combined with the other methods.
///
/// Set `significantBits` only if the number of significant bits is different from the full range of the data
/// type of `image` (use 0 otherwise). For example, it can be used to specify that a camera has produced
/// 10-bit output, even though the image is of type \ref dip::DT_UINT16.
DIP_EXPORT void ImageWritePNG(
Image const& image,
String const& filename,
dip::uint compressionLevel = 6,
StringSet const& filterChoice = { S::ALL },
dip::uint significantBits = 0
);


/// \brief Reads a numeric array from the NumPy NPY file `filename` and puts it in `out`.
///
/// The function tries to open `filename` as given first, and if that fails, it appends ".npy" to the
Expand Down
12 changes: 11 additions & 1 deletion include/diplib/library/stringparams.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* (c)2017-2021, Cris Luengo.
* (c)2017-2024, Cris Luengo.
* Based on original DIPlib code: (c)1995-2014, Delft University of Technology.
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -300,6 +300,16 @@ constexpr char const* MTS = "MTS";
constexpr char const* ITER = "ITER";
constexpr char const* PROJ = "PROJ";

// PNG filter methods
constexpr char const* DISABLE = "disable";
// constexpr char const* NONE = "none";
constexpr char const* SUB = "sub";
constexpr char const* UP = "up";
constexpr char const* AVG = "avg";
constexpr char const* PAETH = "Paeth";
// constexpr char const* ALL = "all";


} // namespace S

} // namespace dip
Expand Down
15 changes: 14 additions & 1 deletion include/diplib/simple_file_io.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* (c)2019-2023, Cris Luengo.
* (c)2019-2024, Cris Luengo.
* Based on original DIPlib code: (c)1995-2014, Delft University of Technology.
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -44,6 +44,7 @@ namespace dip {
/// - `"ics"`: The file is an ICS file, use \ref dip::ImageReadICS.
/// - `"tiff"`: The file is a TIFF file, use \ref dip::ImageReadTIFF. Reads only the first image plane.
/// - `"jpeg"`: The file is a JPEG file, use \ref dip::ImageReadJPEG.
/// - `"png"`: The file is a PNG file, use \ref dip::ImageReadPNG.
/// - `"npy"`: The file is a NumPy NPY file, use \ref dip::ImageReadNPY.
/// - `"bioformats"`: Use \ref dip::javaio::ImageReadJavaIO to read the file with the *Bio-Formats* library.
/// - `""`: Select the format by looking at the file name extension or the file's first few bytes. This is the default.
Expand Down Expand Up @@ -80,6 +81,9 @@ inline FileInformation ImageRead(
} else if( StringCompareCaseInsensitive( format, "jpg" ) || StringCompareCaseInsensitive( format, "jpeg" )) {
DIP_THROW_IF( !ImageIsJPEG( filename ), "File has a JPEG extension but could not be read as a JPEG file" );
format = "jpeg";
} else if( StringCompareCaseInsensitive( format, "png" )) {
DIP_THROW_IF( !ImageIsPNG( filename ), "File has a PNG extension but could not be read as a PNG file" );
format = "png";
} else if( StringCompareCaseInsensitive( format, "npy" )) {
DIP_THROW_IF( !ImageIsNPY( filename ), "File has an NPY extension but could not be read as an NPY file" );
format = "npy";
Expand All @@ -94,6 +98,8 @@ inline FileInformation ImageRead(
format = "tiff";
} else if( ImageIsJPEG( filename )) {
format = "jpeg";
} else if( ImageIsPNG( filename )) {
format = "png";
} else if( ImageIsNPY( filename )) {
format = "npy";
} else {
Expand All @@ -112,6 +118,8 @@ inline FileInformation ImageRead(
DIP_STACK_TRACE_THIS( info = ImageReadTIFF( out, filename ));
} else if( format == "jpeg" ) {
DIP_STACK_TRACE_THIS( info = ImageReadJPEG( out, filename ));
} else if( format == "png" ) {
DIP_STACK_TRACE_THIS( info = ImageReadPNG( out, filename ));
} else if( format == "npy" ) {
DIP_STACK_TRACE_THIS( info = ImageReadNPY( out, filename ));
}
Expand Down Expand Up @@ -142,6 +150,7 @@ DIP_NODISCARD inline Image ImageRead(
/// - `"icsv1"`: Create an ICS version 1 file, use \ref dip::ImageWriteICS.
/// - `"tiff"`: Create a TIFF file, use \ref dip::ImageWriteTIFF.
/// - `"jpeg"`: Create a JPEG file, use \ref dip::ImageWriteJPEG.
/// - `"png"`: Create a PNG file, use \ref dip::ImageWritePNG.
/// - `"npy"`: Create a NumPy NPY file, use \ref dip::ImageWriteNPY.
/// - `""`: Select the format by looking at the file name extension. If no extension is
/// present, it defaults to ICS version 2. This is the default.
Expand Down Expand Up @@ -184,6 +193,8 @@ inline void ImageWrite(
format = "tiff";
} else if( StringCompareCaseInsensitive( format, "jpg" ) || StringCompareCaseInsensitive( format, "jpeg" )) {
format = "jpeg";
} else if( StringCompareCaseInsensitive( format, "png" )) {
format = "png";
} else if( StringCompareCaseInsensitive( format, "npy" )) {
format = "npy";
} else {
Expand All @@ -210,6 +221,8 @@ inline void ImageWrite(
DIP_STACK_TRACE_THIS( ImageWriteTIFF( image, filename, compression ));
} else if( format == "jpeg" ) {
DIP_STACK_TRACE_THIS( ImageWriteJPEG( image, filename ));
} else if( format == "png" ) {
DIP_STACK_TRACE_THIS( ImageWritePNG( image, filename, compression == "none" ? 0 : 6 ));
} else if( format == "npy" ) {
DIP_STACK_TRACE_THIS( ImageWriteNPY( image, filename ));
} else {
Expand Down
1 change: 1 addition & 0 deletions src/DIPlib_sources.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ file_io/file_io_support.h
file_io/ics.cpp
file_io/jpeg.cpp
file_io/npy.cpp
file_io/png.cpp
file_io/tiff_read.cpp
file_io/tiff_write.cpp
generation/coordinates.cpp
Expand Down
Loading

0 comments on commit dae9724

Please sign in to comment.