221 lines
7.3 KiB
C
221 lines
7.3 KiB
C
|
/*
|
||
|
*
|
||
|
* Copyright (C) 2002-2016, OFFIS e.V.
|
||
|
* All rights reserved. See COPYRIGHT file for details.
|
||
|
*
|
||
|
* This software and supporting documentation were developed by
|
||
|
*
|
||
|
* OFFIS e.V.
|
||
|
* R&D Division Health
|
||
|
* Escherweg 2
|
||
|
* D-26121 Oldenburg, Germany
|
||
|
*
|
||
|
*
|
||
|
* Module: dcmimage
|
||
|
*
|
||
|
* Author: Marco Eichelberg
|
||
|
*
|
||
|
* Purpose: class DcmQuantFloydSteinberg
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
|
||
|
#ifndef DIQTFS_H
|
||
|
#define DIQTFS_H
|
||
|
|
||
|
|
||
|
#include "dcmtk/config/osconfig.h"
|
||
|
#include "dcmtk/dcmimage/diqtpix.h" /* for DcmQuantPixel */
|
||
|
#include "dcmtk/ofstd/ofcond.h" /* for OFCondition */
|
||
|
|
||
|
|
||
|
/** Floyd-Steinberg error vectors are stored internally as integer numbers
|
||
|
* containing the error multiplied by this constant.
|
||
|
*/
|
||
|
#define DcmQuantFloydSteinbergScale 1024
|
||
|
|
||
|
|
||
|
/** this class implements Floyd-Steinberg error diffusion.
|
||
|
* It is used during the color quantization of an image.
|
||
|
*/
|
||
|
class DCMTK_DCMIMAGE_EXPORT DcmQuantFloydSteinberg
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
/// constructor
|
||
|
DcmQuantFloydSteinberg();
|
||
|
|
||
|
/// destructor
|
||
|
~DcmQuantFloydSteinberg();
|
||
|
|
||
|
/** initializes the Floyd-Steinberg error vectors for an image with the
|
||
|
* given number of columns.
|
||
|
* @param cols number of columns in image
|
||
|
* @return EC_Normal if successful, an error code otherwise.
|
||
|
*/
|
||
|
OFCondition initialize(unsigned long cols);
|
||
|
|
||
|
/** uses the Floyd-Steinberg error vectors to adjust the color of the current image pixel.
|
||
|
* @param px the original image pixel is passed in this parameter. Upon return, the pixel
|
||
|
* value contains the new value after error diffusion.
|
||
|
* @param col column in which the current pixel is located, must be [0..columns-1]
|
||
|
* @param maxval maximum value for each color component.
|
||
|
*/
|
||
|
inline void adjust(DcmQuantPixel& px, long col, long maxval)
|
||
|
{
|
||
|
long sr = px.getRed() + thisrerr[col + 1] / DcmQuantFloydSteinbergScale;
|
||
|
long sg = px.getGreen() + thisgerr[col + 1] / DcmQuantFloydSteinbergScale;
|
||
|
long sb = px.getBlue() + thisberr[col + 1] / DcmQuantFloydSteinbergScale;
|
||
|
if ( sr < 0 ) sr = 0;
|
||
|
else if ( sr > OFstatic_cast(long, maxval) ) sr = maxval;
|
||
|
if ( sg < 0 ) sg = 0;
|
||
|
else if ( sg > OFstatic_cast(long, maxval) ) sg = maxval;
|
||
|
if ( sb < 0 ) sb = 0;
|
||
|
else if ( sb > OFstatic_cast(long, maxval) ) sb = maxval;
|
||
|
px.assign(OFstatic_cast(DcmQuantComponent, sr), OFstatic_cast(DcmQuantComponent, sg), OFstatic_cast(DcmQuantComponent, sb));
|
||
|
}
|
||
|
|
||
|
/** propagates the Floyd-Steinberg error terms for one pixel.
|
||
|
* @param px color value the current image pixel should have (after adjustment)
|
||
|
* @param mapped color value (selected from the color LUT) the current image pixel really uses
|
||
|
* @param col column in which the current pixel is located, must be [0..columns-1]
|
||
|
*/
|
||
|
inline void propagate(const DcmQuantPixel& px, const DcmQuantPixel& mapped, long col)
|
||
|
{
|
||
|
long err;
|
||
|
|
||
|
/* Propagate Floyd-Steinberg error terms. */
|
||
|
if ( fs_direction )
|
||
|
{
|
||
|
err = ( OFstatic_cast(long, px.getRed()) - OFstatic_cast(long, mapped.getRed()) ) * DcmQuantFloydSteinbergScale;
|
||
|
thisrerr[col + 2] += ( err * 7 ) / 16;
|
||
|
nextrerr[col ] += ( err * 3 ) / 16;
|
||
|
nextrerr[col + 1] += ( err * 5 ) / 16;
|
||
|
nextrerr[col + 2] += ( err ) / 16;
|
||
|
err = ( OFstatic_cast(long, px.getGreen()) - OFstatic_cast(long, mapped.getGreen()) ) * DcmQuantFloydSteinbergScale;
|
||
|
thisgerr[col + 2] += ( err * 7 ) / 16;
|
||
|
nextgerr[col ] += ( err * 3 ) / 16;
|
||
|
nextgerr[col + 1] += ( err * 5 ) / 16;
|
||
|
nextgerr[col + 2] += ( err ) / 16;
|
||
|
err = ( OFstatic_cast(long, px.getBlue()) - OFstatic_cast(long, mapped.getBlue()) ) * DcmQuantFloydSteinbergScale;
|
||
|
thisberr[col + 2] += ( err * 7 ) / 16;
|
||
|
nextberr[col ] += ( err * 3 ) / 16;
|
||
|
nextberr[col + 1] += ( err * 5 ) / 16;
|
||
|
nextberr[col + 2] += ( err ) / 16;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
err = ( OFstatic_cast(long, px.getRed()) - OFstatic_cast(long, mapped.getRed()) ) * DcmQuantFloydSteinbergScale;
|
||
|
thisrerr[col ] += ( err * 7 ) / 16;
|
||
|
nextrerr[col + 2] += ( err * 3 ) / 16;
|
||
|
nextrerr[col + 1] += ( err * 5 ) / 16;
|
||
|
nextrerr[col ] += ( err ) / 16;
|
||
|
err = ( OFstatic_cast(long, px.getGreen()) - OFstatic_cast(long, mapped.getGreen()) ) * DcmQuantFloydSteinbergScale;
|
||
|
thisgerr[col ] += ( err * 7 ) / 16;
|
||
|
nextgerr[col + 2] += ( err * 3 ) / 16;
|
||
|
nextgerr[col + 1] += ( err * 5 ) / 16;
|
||
|
nextgerr[col ] += ( err ) / 16;
|
||
|
err = ( OFstatic_cast(long, px.getBlue()) - OFstatic_cast(long, mapped.getBlue()) ) * DcmQuantFloydSteinbergScale;
|
||
|
thisberr[col ] += ( err * 7 ) / 16;
|
||
|
nextberr[col + 2] += ( err * 3 ) / 16;
|
||
|
nextberr[col + 1] += ( err * 5 ) / 16;
|
||
|
nextberr[col ] += ( err ) / 16;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** starts error diffusion for a new row.
|
||
|
* The error vectors for the next image row are initialized to zero.
|
||
|
* The initial and last column of the current row are determined
|
||
|
* @param col initial column for the current row returned in this parameter
|
||
|
* @param limitcol limit column (one past the last valid column) for the
|
||
|
* current row returned in this parameter. May become negative.
|
||
|
*/
|
||
|
inline void startRow(long& col, long& limitcol)
|
||
|
{
|
||
|
for (unsigned long c = 0; c < columns + 2; ++c)
|
||
|
nextrerr[c] = nextgerr[c] = nextberr[c] = 0;
|
||
|
|
||
|
if (fs_direction)
|
||
|
{
|
||
|
col = 0;
|
||
|
limitcol = columns;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
col = columns - 1;
|
||
|
limitcol = -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** finishes error diffusion for one image row. The direction flag is
|
||
|
* inverted and the error vectors for the "current" and "next" image row
|
||
|
* are swapped.
|
||
|
*/
|
||
|
inline void finishRow()
|
||
|
{
|
||
|
temperr = thisrerr;
|
||
|
thisrerr = nextrerr;
|
||
|
nextrerr = temperr;
|
||
|
temperr = thisgerr;
|
||
|
thisgerr = nextgerr;
|
||
|
nextgerr = temperr;
|
||
|
temperr = thisberr;
|
||
|
thisberr = nextberr;
|
||
|
nextberr = temperr;
|
||
|
fs_direction = ! fs_direction;
|
||
|
}
|
||
|
|
||
|
/** increases or decreases the column number
|
||
|
* depending on the direction flag.
|
||
|
* @param col column number, may become negative
|
||
|
*/
|
||
|
inline void nextCol(long& col) const
|
||
|
{
|
||
|
if (fs_direction) ++col; else --col;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
|
||
|
/// frees all memory allocated by the error vectors
|
||
|
void cleanup();
|
||
|
|
||
|
/// private undefined copy constructor
|
||
|
DcmQuantFloydSteinberg(const DcmQuantFloydSteinberg& src);
|
||
|
|
||
|
/// private undefined copy assignment operator
|
||
|
DcmQuantFloydSteinberg& operator=(const DcmQuantFloydSteinberg& src);
|
||
|
|
||
|
/// current red error vector. Points to an array of (columns + 2) entries.
|
||
|
long *thisrerr;
|
||
|
|
||
|
/// red error vector for next row. Points to an array of (columns + 2) entries.
|
||
|
long *nextrerr;
|
||
|
|
||
|
/// current green error vector. Points to an array of (columns + 2) entries.
|
||
|
long *thisgerr;
|
||
|
|
||
|
/// green error vector for next row. Points to an array of (columns + 2) entries.
|
||
|
long *nextgerr;
|
||
|
|
||
|
/// current blue error vector. Points to an array of (columns + 2) entries.
|
||
|
long *thisberr;
|
||
|
|
||
|
/// blue error vector for next row. Points to an array of (columns + 2) entries.
|
||
|
long *nextberr;
|
||
|
|
||
|
/// temporary pointer used for swapping error vectors
|
||
|
long *temperr;
|
||
|
|
||
|
/** boolean flag indicating in which direction (left to right/right to left)
|
||
|
* the FS distribution should be done. Flag is inverted after each row.
|
||
|
*/
|
||
|
int fs_direction;
|
||
|
|
||
|
/// number of columns in image
|
||
|
unsigned long columns;
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
#endif
|