DYT/Tool/3rdParty_x64/include/dcmtk/dcmimgle/discalet.h
2024-11-22 23:19:31 +08:00

1240 lines
54 KiB
C++

/*
*
* Copyright (C) 1996-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: dcmimgle
*
* Author: Joerg Riesmeier
*
* Purpose: DicomScaleTemplates (Header)
*
*/
#ifndef DISCALET_H
#define DISCALET_H
#include "dcmtk/config/osconfig.h"
#include "dcmtk/ofstd/ofcast.h"
#include "dcmtk/dcmimgle/ditranst.h"
#include "dcmtk/dcmimgle/dipxrept.h"
/*---------------------*
* macro definitions *
*---------------------*/
#define SCALE_FACTOR 4096
#define HALFSCALE_FACTOR 2048
/*--------------------*
* helper functions *
*--------------------*/
// help function to set scaling values
static inline void setScaleValues(Uint16 data[],
const Uint16 min,
const Uint16 max)
{
Uint16 remainder = max % min;
Uint16 step0 = max / min;
Uint16 step1 = max / min;
if (remainder > OFstatic_cast(Uint16, min / 2))
{
remainder = min - remainder;
++step0;
} else
++step1;
const double count = OFstatic_cast(double, min) / (OFstatic_cast(double, remainder) + 1);
Uint16 i;
double c = count;
for (i = 0; i < min; ++i)
{
if ((i >= OFstatic_cast(Uint16, c)) && (remainder > 0))
{
--remainder;
c += count;
data[i] = step1;
}
else
data[i] = step0;
}
}
// cubic value interpolation using Catmull-Rom formula.
// the interpolated pixel lies between the second and the third original pixels
static inline double cubicValue(const double v1,
const double v2,
const double v3,
const double v4,
const double dD,
const double minVal,
const double maxVal)
{
double dVal = 0.5 * ((((-v1 + 3 * v2 - 3 * v3 + v4) * dD + (2 * v1 - 5 * v2 + 4 * v3 - v4)) * dD + (-v1 + v3)) * dD + (v2 + v2));
return (dVal < minVal) ? minVal : ((dVal > maxVal) ? maxVal : dVal);
}
/*---------------------*
* class declaration *
*---------------------*/
/** Template class to scale images (on pixel data level).
* with and without interpolation
*/
template<class T>
class DiScaleTemplate
: public DiTransTemplate<T>
{
public:
/** constructor, scale clipping area.
*
** @param planes number of planes (1 or 3)
* @param columns width of source image
* @param rows height of source image
* @param left_pos left coordinate of clipping area
* @param top_pos top coordinate of clipping area
* @param src_cols width of clipping area
* @param src_rows height of clipping area
* @param dest_cols width of destination image (scaled image)
* @param dest_rows height of destination image
* @param frames number of frames
* @param bits number of bits per plane/pixel
*/
DiScaleTemplate(const int planes,
const Uint16 columns, /* resolution of source image */
const Uint16 rows,
const signed long left_pos, /* origin of clipping area */
const signed long top_pos,
const Uint16 src_cols, /* extension of clipping area */
const Uint16 src_rows,
const Uint16 dest_cols, /* extension of destination image */
const Uint16 dest_rows,
const Uint32 frames, /* number of frames */
const int bits = 0)
: DiTransTemplate<T>(planes, src_cols, src_rows, dest_cols, dest_rows, frames, bits),
Left(left_pos),
Top(top_pos),
Columns(columns),
Rows(rows)
{
}
/** constructor, scale whole image.
*
** @param planes number of planes (1 or 3)
* @param src_cols width of source image
* @param src_rows height of source image
* @param dest_cols width of destination image (scaled image)
* @param dest_rows height of destination image
* @param frames number of frames
* @param bits number of bits per plane/pixel
*/
DiScaleTemplate(const int planes,
const Uint16 src_cols, /* resolution of source image */
const Uint16 src_rows,
const Uint16 dest_cols, /* resolution of destination image */
const Uint16 dest_rows,
const Uint32 frames, /* number of frames */
const int bits = 0)
: DiTransTemplate<T>(planes, src_cols, src_rows, dest_cols, dest_rows, frames, bits),
Left(0),
Top(0),
Columns(src_cols),
Rows(src_rows)
{
}
/** destructor
*/
virtual ~DiScaleTemplate()
{
}
/** check whether template type T is signed or not
*
** @return true if signed, false otherwise
*/
inline int isSigned() const
{
const DiPixelRepresentationTemplate<T> rep;
return rep.isSigned();
}
/** choose scaling/clipping algorithm depending on specified parameters.
*
** @param src array of pointers to source image pixels
* @param dest array of pointers to destination image pixels
* @param interpolate preferred interpolation algorithm (0 = no interpolation, 1 = pbmplus algorithm,
* 2 = c't algorithm, 3 = bilinear magnification, 4 = bicubic magnification)
* @param value value to be set outside the image boundaries (used for clipping, default: 0)
*/
void scaleData(const T *src[],
T *dest[],
const int interpolate,
const T value = 0)
{
if ((src != NULL) && (dest != NULL))
{
DCMIMGLE_TRACE("Col/Rows: " << Columns << " " << Rows << OFendl
<< "Left/Top: " << Left << " " << Top << OFendl
<< "Src X/Y: " << this->Src_X << " " << this->Src_Y << OFendl
<< "Dest X/Y: " << this->Dest_X << " " << this->Dest_Y);
if ((Left + OFstatic_cast(signed long, this->Src_X) <= 0) || (Top + OFstatic_cast(signed long, this->Src_Y) <= 0) ||
(Left >= OFstatic_cast(signed long, Columns)) || (Top >= OFstatic_cast(signed long, Rows)))
{ // no image to be displayed
DCMIMGLE_DEBUG("clipping area is fully outside the image boundaries");
this->fillPixel(dest, value); // ... fill bitmap
}
else if ((this->Src_X == this->Dest_X) && (this->Src_Y == this->Dest_Y)) // no scaling
{
if ((Left == 0) && (Top == 0) && (Columns == this->Src_X) && (Rows == this->Src_Y))
this->copyPixel(src, dest); // copying
else if ((Left >= 0) && (OFstatic_cast(Uint16, Left + this->Src_X) <= Columns) &&
(Top >= 0) && (OFstatic_cast(Uint16, Top + this->Src_Y) <= Rows))
clipPixel(src, dest); // clipping
else
clipBorderPixel(src, dest, value); // clipping (with border)
}
else if ((interpolate == 1) && (this->Bits <= MAX_INTERPOLATION_BITS))
interpolatePixel(src, dest); // interpolation (pbmplus)
else if ((interpolate == 4) && (this->Dest_X >= this->Src_X) && (this->Dest_Y >= this->Src_Y) &&
(this->Src_X >= 3) && (this->Src_Y >= 3))
bicubicPixel(src, dest); // bicubic magnification
else if ((interpolate >= 3) && (this->Dest_X >= this->Src_X) && (this->Dest_Y >= this->Src_Y) &&
(this->Src_X >= 2) && (this->Src_Y >= 2))
bilinearPixel(src, dest); // bilinear magnification
else if ((interpolate >= 1) && (this->Dest_X >= this->Src_X) && (this->Dest_Y >= this->Src_Y))
expandPixel(src, dest); // interpolated expansion (c't)
else if ((interpolate >= 1) && (this->Src_X >= this->Dest_X) && (this->Src_Y >= this->Dest_Y))
reducePixel(src, dest); // interpolated reduction (c't)
else if ((interpolate >= 1) && (this->Bits <= MAX_INTERPOLATION_BITS))
interpolatePixel(src, dest); // interpolation (pbmplus), fallback
else if ((this->Dest_X % this->Src_X == 0) && (this->Dest_Y % this->Src_Y == 0))
replicatePixel(src, dest); // replication
else if ((this->Src_X % this->Dest_X == 0) && (this->Src_Y % this->Dest_Y == 0))
suppressPixel(src, dest); // suppression
else
scalePixel(src, dest); // general scaling
}
}
protected:
/// left coordinate of clipping area
const signed long Left;
/// top coordinate of clipping area
const signed long Top;
/// width of source image
const Uint16 Columns;
/// height of source image
const Uint16 Rows;
private:
/** clip image to specified area (only inside image boundaries).
* This is an optimization of the more general method clipBorderPixel().
*
** @param src array of pointers to source image pixels
* @param dest array of pointers to destination image pixels
*/
void clipPixel(const T *src[],
T *dest[])
{
DCMIMGLE_DEBUG("using clip image to specified area algorithm");
const unsigned long x_feed = Columns - this->Src_X;
const unsigned long y_feed = OFstatic_cast(unsigned long, Rows - this->Src_Y) * OFstatic_cast(unsigned long, Columns);
Uint16 x;
Uint16 y;
const T *p;
T *q;
for (int j = 0; j < this->Planes; ++j)
{
p = src[j] + OFstatic_cast(unsigned long, Top) * OFstatic_cast(unsigned long, Columns) + Left;
q = dest[j];
for (unsigned long f = this->Frames; f != 0; --f)
{
for (y = this->Dest_Y; y != 0; --y)
{
for (x = this->Dest_X; x != 0; --x)
*(q++) = *(p++);
p += x_feed;
}
p += y_feed;
}
}
}
/** clip image to specified area and add a border if necessary
*
** @param src array of pointers to source image pixels
* @param dest array of pointers to destination image pixels
* @param value value to be set outside the image boundaries
*/
void clipBorderPixel(const T *src[],
T *dest[],
const T value)
{
DCMIMGLE_DEBUG("using clip image to specified area and add border algorithm");
const Uint16 s_left = (Left > 0) ? OFstatic_cast(Uint16, Left) : 0;
const Uint16 s_top = (Top > 0) ? OFstatic_cast(Uint16, Top) : 0;
const Uint16 d_left = (Left < 0 ? OFstatic_cast(Uint16, -Left) : 0);
const Uint16 d_top = (Top < 0) ? OFstatic_cast(Uint16, -Top) : 0;
const Uint16 d_right = (OFstatic_cast(unsigned long, this->Src_X) + OFstatic_cast(unsigned long, s_left) <
OFstatic_cast(unsigned long, Columns) + OFstatic_cast(unsigned long, d_left)) ?
(this->Src_X - 1) : (Columns + d_left - s_left - 1);
const Uint16 d_bottom = (OFstatic_cast(unsigned long, this->Src_Y) + OFstatic_cast(unsigned long, s_top) <
OFstatic_cast(unsigned long, Rows) + OFstatic_cast(unsigned long, d_top)) ?
(this->Src_Y - 1) : (Rows + d_top - s_top - 1);
const Uint16 x_count = d_right - d_left + 1;
const Uint16 y_count = d_bottom - d_top + 1;
const unsigned long s_start = OFstatic_cast(unsigned long, s_top) * OFstatic_cast(unsigned long, Columns) + s_left;
const unsigned long x_feed = Columns - x_count;
const unsigned long y_feed = OFstatic_cast(unsigned long, Rows - y_count) * Columns;
const unsigned long t_feed = OFstatic_cast(unsigned long, d_top) * OFstatic_cast(unsigned long, this->Src_X);
const unsigned long b_feed = OFstatic_cast(unsigned long, this->Src_Y - d_bottom - 1) * OFstatic_cast(unsigned long, this->Src_X);
/*
* The approach is to divide the destination image in up to four areas outside the source image
* plus one area for the source image. The for and while loops are scanning linearly over the
* destination image and setting the appropriate value depending on the current area. This is
* different from most of the other algorithms in this toolkit where the source image is scanned
* linearly.
*/
Uint16 x;
Uint16 y;
unsigned long i;
const T *p;
T *q;
for (int j = 0; j < this->Planes; ++j)
{
p = src[j] + s_start;
q = dest[j];
for (unsigned long f = this->Frames; f != 0; --f)
{
for (i = t_feed; i != 0; --i) // top
*(q++) = value;
for (y = y_count; y != 0; --y) // middle part:
{
x = 0;
while (x < d_left) // - left
{
*(q++) = value;
++x;
}
while (x <= d_right) // - middle
{
*(q++) = *(p++);
++x;
}
while (x < this->Src_X) // - right
{
*(q++) = value;
++x;
}
p += x_feed;
}
for (i = b_feed; i != 0; --i) // bottom
*(q++) = value;
p += y_feed;
}
}
}
/** enlarge image by an integer factor.
* Pixels are replicated independently in both directions.
*
** @param src array of pointers to source image pixels
* @param dest array of pointers to destination image pixels
*/
void replicatePixel(const T *src[],
T *dest[])
{
DCMIMGLE_DEBUG("using replicate pixel scaling algorithm without interpolation");
const Uint16 x_factor = this->Dest_X / this->Src_X;
const Uint16 y_factor = this->Dest_Y / this->Src_Y;
const unsigned long x_feed = Columns;
const unsigned long y_feed = OFstatic_cast(unsigned long, Rows - this->Src_Y) * OFstatic_cast(unsigned long, Columns);
const T *sp;
Uint16 x;
Uint16 y;
Uint16 dx;
Uint16 dy;
const T *p;
T *q;
T value;
for (int j = 0; j < this->Planes; ++j)
{
sp = src[j] + OFstatic_cast(unsigned long, Top) * OFstatic_cast(unsigned long, Columns) + Left;
q = dest[j];
for (unsigned long f = this->Frames; f != 0; --f)
{
for (y = this->Src_Y; y != 0; --y)
{
for (dy = y_factor; dy != 0; --dy)
{
for (x = this->Src_X, p = sp; x != 0; --x)
{
value = *(p++);
for (dx = x_factor; dx != 0; --dx)
*(q++) = value;
}
}
sp += x_feed;
}
sp += y_feed;
}
}
}
/** shrink image by an integer divisor.
* Pixels are suppressed independently in both directions.
*
** @param src array of pointers to source image pixels
* @param dest array of pointers to destination image pixels
*/
void suppressPixel(const T *src[],
T *dest[])
{
DCMIMGLE_DEBUG("using suppress pixel scaling algorithm without interpolation");
const unsigned int x_divisor = this->Src_X / this->Dest_X;
const unsigned long x_feed = OFstatic_cast(unsigned long, this->Src_Y / this->Dest_Y) * OFstatic_cast(unsigned long, Columns) - this->Src_X;
const unsigned long y_feed = OFstatic_cast(unsigned long, Rows - this->Src_Y) * OFstatic_cast(unsigned long, Columns);
Uint16 x;
Uint16 y;
const T *p;
T *q;
for (int j = 0; j < this->Planes; ++j)
{
p = src[j] + OFstatic_cast(unsigned long, Top) * OFstatic_cast(unsigned long, Columns) + Left;
q = dest[j];
for (unsigned long f = this->Frames; f != 0; --f)
{
for (y = this->Dest_Y; y != 0; --y)
{
for (x = this->Dest_X; x != 0; --x)
{
*(q++) = *p;
p += x_divisor;
}
p += x_feed;
}
p += y_feed;
}
}
}
/** free scaling method without interpolation.
* This algorithm is necessary for overlays (1 bpp).
*
** @param src array of pointers to source image pixels
* @param dest array of pointers to destination image pixels
*/
void scalePixel(const T *src[],
T *dest[])
{
DCMIMGLE_DEBUG("using free scaling algorithm without interpolation");
const Uint16 xmin = (this->Dest_X < this->Src_X) ? this->Dest_X : this->Src_X; // minimum width
const Uint16 ymin = (this->Dest_Y < this->Src_Y) ? this->Dest_Y : this->Src_Y; // minimum height
Uint16 *x_step = new Uint16[xmin];
Uint16 *y_step = new Uint16[ymin];
Uint16 *x_fact = new Uint16[xmin];
Uint16 *y_fact = new Uint16[ymin];
/*
* Approach: If one pixel line has to be added or removed it is taken from the middle of the image (1/2).
* For two lines it is at 1/3 and 2/3 of the image and so on. It sounds easy but it was a hard job ;-)
*/
if ((x_step != NULL) && (y_step != NULL) && (x_fact != NULL) && (y_fact != NULL))
{
Uint16 x;
Uint16 y;
if (this->Dest_X < this->Src_X)
setScaleValues(x_step, this->Dest_X, this->Src_X);
else if (this->Dest_X > this->Src_X)
setScaleValues(x_fact, this->Src_X, this->Dest_X);
if (this->Dest_X <= this->Src_X)
OFBitmanipTemplate<Uint16>::setMem(x_fact, 1, xmin); // initialize with default values
if (this->Dest_X >= this->Src_X)
OFBitmanipTemplate<Uint16>::setMem(x_step, 1, xmin); // initialize with default values
x_step[xmin - 1] += Columns - this->Src_X; // skip to next line
if (this->Dest_Y < this->Src_Y)
setScaleValues(y_step, this->Dest_Y, this->Src_Y);
else if (this->Dest_Y > this->Src_Y)
setScaleValues(y_fact, this->Src_Y, this->Dest_Y);
if (this->Dest_Y <= this->Src_Y)
OFBitmanipTemplate<Uint16>::setMem(y_fact, 1, ymin); // initialize with default values
if (this->Dest_Y >= this->Src_Y)
OFBitmanipTemplate<Uint16>::setMem(y_step, 1, ymin); // initialize with default values
y_step[ymin - 1] += Rows - this->Src_Y; // skip to next frame
const T *sp;
Uint16 dx;
Uint16 dy;
const T *p;
T *q;
T value;
for (int j = 0; j < this->Planes; ++j)
{
sp = src[j] + OFstatic_cast(unsigned long, Top) * OFstatic_cast(unsigned long, Columns) + Left;
q = dest[j];
for (unsigned long f = 0; f < this->Frames; ++f)
{
for (y = 0; y < ymin; ++y)
{
for (dy = 0; dy < y_fact[y]; ++dy)
{
for (x = 0, p = sp; x < xmin; ++x)
{
value = *p;
for (dx = 0; dx < x_fact[x]; ++dx)
*(q++) = value;
p += x_step[x];
}
}
sp += OFstatic_cast(unsigned long, y_step[y]) * OFstatic_cast(unsigned long, Columns);
}
}
}
}
delete[] x_step;
delete[] y_step;
delete[] x_fact;
delete[] y_fact;
}
/** free scaling method with interpolation
*
** @param src array of pointers to source image pixels
* @param dest array of pointers to destination image pixels
*/
void interpolatePixel(const T *src[],
T *dest[])
{
DCMIMGLE_DEBUG("using scaling algorithm with interpolation from pbmplus toolkit");
if ((this->Src_X != Columns) || (this->Src_Y != Rows))
{
DCMIMGLE_ERROR("interpolated scaling and clipping at the same time not implemented ... ignoring clipping region");
this->Src_X = Columns; // temporarily removed 'const' for 'Src_X' in class 'DiTransTemplate'
this->Src_Y = Rows; // ... 'Src_Y' ...
}
/*
* based on scaling algorithm from "Extended Portable Bitmap Toolkit" (pbmplus10dec91)
* (adapted to be used with signed pixel representation, inverse images - mono1,
* various bit depths, multi-frame and multi-plane/color images)
*/
Uint16 x;
Uint16 y;
const T *p;
T *q;
const T *sp = NULL; // initialization avoids compiler warning
const T *fp;
T *sq;
const unsigned long sxscale = OFstatic_cast(unsigned long, (OFstatic_cast(double, this->Dest_X) / OFstatic_cast(double, this->Src_X)) * SCALE_FACTOR);
const unsigned long syscale = OFstatic_cast(unsigned long, (OFstatic_cast(double, this->Dest_Y) / OFstatic_cast(double, this->Src_Y)) * SCALE_FACTOR);
const signed long maxvalue = DicomImageClass::maxval(this->Bits - isSigned());
T *xtemp = new T[this->Src_X];
signed long *xvalue = new signed long[this->Src_X];
if ((xtemp == NULL) || (xvalue == NULL))
{
DCMIMGLE_ERROR("can't allocate temporary buffers for interpolation scaling");
this->clearPixel(dest);
} else {
for (int j = 0; j < this->Planes; ++j)
{
fp = src[j];
sq = dest[j];
for (unsigned long f = this->Frames; f != 0; --f)
{
for (x = 0; x < this->Src_X; ++x)
xvalue[x] = HALFSCALE_FACTOR;
unsigned long yfill = SCALE_FACTOR;
unsigned long yleft = syscale;
int yneed = 1;
int ysrc = 0;
for (y = 0; y < this->Dest_Y; ++y)
{
if (this->Src_Y == this->Dest_Y)
{
sp = fp;
for (x = this->Src_X, p = sp, q = xtemp; x != 0; --x)
*(q++) = *(p++);
fp += this->Src_X;
}
else
{
while (yleft < yfill)
{
if (yneed && (ysrc < OFstatic_cast(int, this->Src_Y)))
{
sp = fp;
fp += this->Src_X;
++ysrc;
}
for (x = 0, p = sp; x < this->Src_X; ++x)
xvalue[x] += yleft * OFstatic_cast(signed long, *(p++));
yfill -= yleft;
yleft = syscale;
yneed = 1;
}
if (yneed && (ysrc < OFstatic_cast(int, this->Src_Y)))
{
sp = fp;
fp += this->Src_X;
++ysrc;
yneed = 0;
}
signed long v;
for (x = 0, p = sp, q = xtemp; x < this->Src_X; ++x)
{
v = xvalue[x] + yfill * OFstatic_cast(signed long, *(p++));
v /= SCALE_FACTOR;
*(q++) = OFstatic_cast(T, (v > maxvalue) ? maxvalue : v);
xvalue[x] = HALFSCALE_FACTOR;
}
yleft -= yfill;
if (yleft == 0)
{
yleft = syscale;
yneed = 1;
}
yfill = SCALE_FACTOR;
}
if (this->Src_X == this->Dest_X)
{
for (x = this->Dest_X, p = xtemp, q = sq; x != 0; --x)
*(q++) = *(p++);
sq += this->Dest_X;
}
else
{
signed long v = HALFSCALE_FACTOR;
unsigned long xfill = SCALE_FACTOR;
unsigned long xleft;
int xneed = 0;
q = sq;
for (x = 0, p = xtemp; x < this->Src_X; ++x, ++p)
{
xleft = sxscale;
while (xleft >= xfill)
{
if (xneed)
{
++q;
v = HALFSCALE_FACTOR;
}
v += xfill * OFstatic_cast(signed long, *p);
v /= SCALE_FACTOR;
*q = OFstatic_cast(T, (v > maxvalue) ? maxvalue : v);
xleft -= xfill;
xfill = SCALE_FACTOR;
xneed = 1;
}
if (xleft > 0)
{
if (xneed)
{
++q;
v = HALFSCALE_FACTOR;
xneed = 0;
}
v += xleft * OFstatic_cast(signed long, *p);
xfill -= xleft;
}
}
if (xfill > 0)
v += xfill * OFstatic_cast(signed long, *(--p));
if (!xneed)
{
v /= SCALE_FACTOR;
*q = OFstatic_cast(T, (v > maxvalue) ? maxvalue : v);
}
sq += this->Dest_X;
}
}
}
}
}
delete[] xtemp;
delete[] xvalue;
}
/** free scaling method with interpolation (only for magnification)
*
** @param src array of pointers to source image pixels
* @param dest array of pointers to destination image pixels
*/
void expandPixel(const T *src[],
T *dest[])
{
DCMIMGLE_DEBUG("using expand pixel scaling algorithm with interpolation from c't magazine");
const double x_factor = OFstatic_cast(double, this->Src_X) / OFstatic_cast(double, this->Dest_X);
const double y_factor = OFstatic_cast(double, this->Src_Y) / OFstatic_cast(double, this->Dest_Y);
const unsigned long f_size = OFstatic_cast(unsigned long, Rows) * OFstatic_cast(unsigned long, Columns);
const T *sp;
double bx, ex;
double by, ey;
int bxi, exi;
int byi, eyi;
unsigned long offset;
double value, sum;
double x_part, y_part;
double l_factor, r_factor;
double t_factor, b_factor;
int xi;
int yi;
Uint16 x;
Uint16 y;
const T *p;
T *q;
/*
* based on scaling algorithm from "c't - Magazin fuer Computertechnik" (c't 11/94)
* (adapted to be used with signed pixel representation, inverse images - mono1,
* various bit depths, multi-frame and multi-plane/color images, combined clipping/scaling)
*/
for (int j = 0; j < this->Planes; ++j)
{
sp = src[j] + OFstatic_cast(unsigned long, Top) * OFstatic_cast(unsigned long, Columns) + Left;
q = dest[j];
for (unsigned long f = 0; f < this->Frames; ++f)
{
for (y = 0; y < this->Dest_Y; ++y)
{
by = y_factor * OFstatic_cast(double, y);
ey = y_factor * (OFstatic_cast(double, y) + 1.0);
if (ey > this->Src_Y)
{
#ifdef DEBUG // this output is only useful for debugging purposes
DCMIMGLE_TRACE(" limiting value of 'ey' to 'Src_Y': " << ey << " -> " << this->Src_Y);
#endif
// see reducePixel()
ey = this->Src_Y;
}
byi = OFstatic_cast(int, by);
eyi = OFstatic_cast(int, ey);
if (OFstatic_cast(double, eyi) == ey)
{
#ifdef DEBUG // this output is only useful for debugging purposes
DCMIMGLE_TRACE(" decreasing value of 'eyi' by 1: " << eyi << " -> " << (eyi - 1));
#endif
--eyi;
}
y_part = OFstatic_cast(double, eyi) / y_factor;
b_factor = y_part - OFstatic_cast(double, y);
t_factor = (OFstatic_cast(double, y) + 1.0) - y_part;
for (x = 0; x < this->Dest_X; ++x)
{
value = 0;
bx = x_factor * OFstatic_cast(double, x);
ex = x_factor * (OFstatic_cast(double, x) + 1.0);
if (ex > this->Src_X)
{
#ifdef DEBUG // this output is only useful for debugging purposes
DCMIMGLE_TRACE(" limiting value of 'ex' to 'Src_X': " << ex << " -> " << this->Src_X);
#endif
// see reducePixel()
ex = this->Src_X;
}
bxi = OFstatic_cast(int, bx);
exi = OFstatic_cast(int, ex);
if (OFstatic_cast(double, exi) == ex)
{
#ifdef DEBUG // this output is only useful for debugging purposes
DCMIMGLE_TRACE(" decreasing value of 'exi' by 1: " << exi << " -> " << (exi - 1));
#endif
--exi;
}
x_part = OFstatic_cast(double, exi) / x_factor;
l_factor = x_part - OFstatic_cast(double, x);
r_factor = (OFstatic_cast(double, x) + 1.0) - x_part;
offset = OFstatic_cast(unsigned long, byi) * OFstatic_cast(unsigned long, Columns);
for (yi = byi; yi <= eyi; ++yi)
{
p = sp + offset + bxi;
for (xi = bxi; xi <= exi; ++xi)
{
sum = OFstatic_cast(double, *(p++));
if (bxi != exi)
{
if (xi == bxi)
sum *= l_factor;
else
sum *= r_factor;
}
if (byi != eyi)
{
if (yi == byi)
sum *= b_factor;
else
sum *= t_factor;
}
value += sum;
}
offset += Columns;
}
*(q++) = OFstatic_cast(T, value + 0.5);
}
}
sp += f_size;
}
}
}
/** free scaling method with interpolation (only for reduction)
*
** @param src array of pointers to source image pixels
* @param dest array of pointers to destination image pixels
*/
void reducePixel(const T *src[],
T *dest[])
{
DCMIMGLE_DEBUG("using reduce pixel scaling algorithm with interpolation from c't magazine");
const double x_factor = OFstatic_cast(double, this->Src_X) / OFstatic_cast(double, this->Dest_X);
const double y_factor = OFstatic_cast(double, this->Src_Y) / OFstatic_cast(double, this->Dest_Y);
const double xy_factor = x_factor * y_factor;
const unsigned long f_size = OFstatic_cast(unsigned long, Rows) * OFstatic_cast(unsigned long, Columns);
const T *sp;
double bx, ex;
double by, ey;
int bxi, exi;
int byi, eyi;
unsigned long offset;
double value, sum;
double l_factor, r_factor;
double t_factor, b_factor;
int xi;
int yi;
Uint16 x;
Uint16 y;
const T *p;
T *q;
/*
* based on scaling algorithm from "c't - Magazin fuer Computertechnik" (c't 11/94)
* (adapted to be used with signed pixel representation, inverse images - mono1,
* various bit depths, multi-frame and multi-plane/color images, combined clipping/scaling)
*/
for (int j = 0; j < this->Planes; ++j)
{
sp = src[j] + OFstatic_cast(unsigned long, Top) * OFstatic_cast(unsigned long, Columns) + Left;
q = dest[j];
for (unsigned long f = 0; f < this->Frames; ++f)
{
for (y = 0; y < this->Dest_Y; ++y)
{
by = y_factor * OFstatic_cast(double, y);
ey = y_factor * (OFstatic_cast(double, y) + 1.0);
if (ey > this->Src_Y)
{
#ifdef DEBUG // this output is only useful for debugging purposes
DCMIMGLE_TRACE(" limiting value of 'ey' to 'Src_Y': " << ey << " -> " << this->Src_Y);
#endif
// yes, this can happen due to rounding, e.g. double(943) / double(471) * double(471)
// is something like 943.00000000000011368683772161602974 and then, the eyi == ey check
// fails to bring eyi back into range!
ey = this->Src_Y;
}
byi = OFstatic_cast(int, by);
eyi = OFstatic_cast(int, ey);
if (OFstatic_cast(double, eyi) == ey)
{
#ifdef DEBUG // this output is only useful for debugging purposes
DCMIMGLE_TRACE(" decreasing value of 'eyi' by 1: " << eyi << " -> " << (eyi - 1));
#endif
--eyi;
}
b_factor = 1 + OFstatic_cast(double, byi) - by;
t_factor = ey - OFstatic_cast(double, eyi);
for (x = 0; x < this->Dest_X; ++x)
{
value = 0;
bx = x_factor * OFstatic_cast(double, x);
ex = x_factor * (OFstatic_cast(double, x) + 1.0);
if (ex > this->Src_X)
{
#ifdef DEBUG // this output is only useful for debugging purposes
DCMIMGLE_TRACE(" limiting value of 'ex' to 'Src_X': " << ex << " -> " << this->Src_X);
#endif
// see above comment
ex = this->Src_X;
}
bxi = OFstatic_cast(int, bx);
exi = OFstatic_cast(int, ex);
if (OFstatic_cast(double, exi) == ex)
{
#ifdef DEBUG // this output is only useful for debugging purposes
DCMIMGLE_TRACE(" decreasing value of 'exi' by 1: " << exi << " -> " << (exi - 1));
#endif
--exi;
}
l_factor = 1 + OFstatic_cast(double, bxi) - bx;
r_factor = ex - OFstatic_cast(double, exi);
offset = OFstatic_cast(unsigned long, byi) * OFstatic_cast(unsigned long, Columns);
for (yi = byi; yi <= eyi; ++yi)
{
p = sp + offset + bxi;
for (xi = bxi; xi <= exi; ++xi)
{
sum = OFstatic_cast(double, *(p++)) / xy_factor;
if (xi == bxi)
sum *= l_factor;
else if (xi == exi)
sum *= r_factor;
if (yi == byi)
sum *= b_factor;
else if (yi == eyi)
sum *= t_factor;
value += sum;
}
offset += Columns;
}
*(q++) = OFstatic_cast(T, value + 0.5);
}
}
sp += f_size;
}
}
}
/** bilinear interpolation method (only for magnification)
*
** @param src array of pointers to source image pixels
* @param dest array of pointers to destination image pixels
*/
void bilinearPixel(const T *src[],
T *dest[])
{
DCMIMGLE_DEBUG("using magnification algorithm with bilinear interpolation contributed by Eduard Stanescu");
const double x_factor = OFstatic_cast(double, this->Src_X) / OFstatic_cast(double, this->Dest_X);
const double y_factor = OFstatic_cast(double, this->Src_Y) / OFstatic_cast(double, this->Dest_Y);
const unsigned long f_size = OFstatic_cast(unsigned long, Rows) * OFstatic_cast(unsigned long, Columns);
const unsigned long l_offset = OFstatic_cast(unsigned long, this->Src_Y - 1) * OFstatic_cast(unsigned long, this->Dest_X);
Uint16 x;
Uint16 y;
T *pD;
T *pCurrTemp;
const T *pCurrSrc;
Uint16 nSrcIndex;
double dOff;
T *pT;
const T *pS;
const T *pF;
// buffer used for storing temporarily the interpolated lines
T *pTemp = new T[OFstatic_cast(unsigned long, this->Src_Y) * OFstatic_cast(unsigned long, this->Dest_X)];
if (pTemp == NULL)
{
DCMIMGLE_ERROR("can't allocate temporary buffer for interpolation scaling");
this->clearPixel(dest);
} else {
/*
* based on scaling algorithm contributed by Eduard Stanescu
* (adapted to be used with signed pixel representation, inverse images - mono1,
* various bit depths, multi-frame multi-plane/color images, combined clipping/scaling)
*/
for (int j = 0; j < this->Planes; ++j)
{
pF = src[j] + OFstatic_cast(unsigned long, Top) * OFstatic_cast(unsigned long, Columns) + Left;
pD = dest[j];
for (unsigned long f = this->Frames; f != 0; --f)
{
pT = pCurrTemp = pTemp;
pS = pCurrSrc = pF;
// first, interpolate the columns:
// column 0, just copy the source data column 0
for (y = this->Src_Y; y != 0; --y)
{
*(pCurrTemp) = *(pCurrSrc);
pCurrSrc += Columns;
pCurrTemp += this->Dest_X;
}
pCurrSrc = pS;
nSrcIndex = 0;
// column 1 to column Dest_X - 1
for (x = 1; x < this->Dest_X - 1; ++x)
{
pCurrTemp = ++pT;
dOff = x * x_factor - nSrcIndex;
dOff = (1.0 < dOff) ? 1.0 : dOff;
for (y = 0; y < this->Src_Y; ++y)
{
// use floating points in order to avoid possible integer overflow
const double v1 = OFstatic_cast(double, *(pCurrSrc));
const double v2 = OFstatic_cast(double, *(pCurrSrc + 1));
*(pCurrTemp) = OFstatic_cast(T, v1 + (v2 - v1) * dOff);
pCurrSrc += Columns;
pCurrTemp += this->Dest_X;
}
// don't go beyond the source data
if ((nSrcIndex < this->Src_X - 2) && (x * x_factor >= nSrcIndex + 1))
{
pS++;
nSrcIndex++;
}
pCurrSrc = pS;
}
pCurrTemp = ++pT;
// last column, just copy the source data column Src_X
for (y = this->Src_Y; y != 0; --y)
{
*(pCurrTemp) = *(pCurrSrc);
pCurrSrc += Columns;
pCurrTemp += this->Dest_X;
}
// now the columns are interpolated in temp buffer, so interpolate the lines
pT = pCurrTemp = pTemp;
// line 0, just copy the temp buffer line 0
for (x = this->Dest_X; x != 0; --x)
*(pD++) = *(pCurrTemp++);
nSrcIndex = 0;
pCurrTemp = pTemp;
for (y = 1; y < this->Dest_Y - 1; ++y)
{
dOff = y * y_factor - nSrcIndex;
dOff = (1.0 < dOff) ? 1.0 : dOff;
for (x = this->Dest_X; x != 0; --x)
{
// use floating points in order to avoid possible integer overflow
const double v1 = OFstatic_cast(double, *(pCurrTemp));
const double v2 = OFstatic_cast(double, *(pCurrTemp + this->Dest_X));
*(pD++) = OFstatic_cast(T, v1 + (v2 - v1) * dOff);
pCurrTemp++;
}
// don't go beyond the source data
if ((nSrcIndex < this->Src_Y - 2) && (y * y_factor >= nSrcIndex + 1))
{
pT += this->Dest_X;
nSrcIndex++;
}
pCurrTemp = pT;
}
// the last line, just copy the temp buffer line Src_X
pCurrTemp = pTemp + l_offset;
for (x = this->Dest_X; x != 0; --x)
*(pD++) = *(pCurrTemp++);
// skip to next frame
pF += f_size;
}
}
}
delete[] pTemp;
}
/** bicubic interpolation method (only for magnification)
*
** @param src array of pointers to source image pixels
* @param dest array of pointers to destination image pixels
*/
void bicubicPixel(const T *src[],
T *dest[])
{
DCMIMGLE_DEBUG("using magnification algorithm with bicubic interpolation contributed by Eduard Stanescu");
const double minVal = (isSigned()) ? -OFstatic_cast(double, DicomImageClass::maxval(this->Bits - 1, 0)) : 0.0;
const double maxVal = OFstatic_cast(double, DicomImageClass::maxval(this->Bits - isSigned()));
const double x_factor = OFstatic_cast(double, this->Src_X) / OFstatic_cast(double, this->Dest_X);
const double y_factor = OFstatic_cast(double, this->Src_Y) / OFstatic_cast(double, this->Dest_Y);
const Uint16 xDelta = OFstatic_cast(Uint16, 1 / x_factor);
const Uint16 yDelta = OFstatic_cast(Uint16, 1 / y_factor);
const unsigned long f_size = OFstatic_cast(unsigned long, Rows) * OFstatic_cast(unsigned long, Columns);
const unsigned long l_offset = OFstatic_cast(unsigned long, this->Src_Y - 1) * OFstatic_cast(unsigned long, this->Dest_X);
Uint16 x;
Uint16 y;
T *pD;
T *pCurrTemp;
const T *pCurrSrc;
Uint16 nSrcIndex;
double dOff;
T *pT;
const T *pS;
const T *pF;
// buffer used for storing temporarily the interpolated lines
T *pTemp = pT = pCurrTemp = new T[OFstatic_cast(unsigned long, this->Src_Y) * OFstatic_cast(unsigned long, this->Dest_X)];
if (pTemp == NULL)
{
DCMIMGLE_ERROR("can't allocate temporary buffer for interpolation scaling");
this->clearPixel(dest);
} else {
/*
* based on scaling algorithm contributed by Eduard Stanescu
* (adapted to be used with signed pixel representation, inverse images - mono1,
* various bit depths, multi-frame multi-plane/color images, combined clipping/scaling)
*/
for (int j = 0; j < this->Planes; ++j)
{
pF = src[j] + OFstatic_cast(unsigned long, Top) * OFstatic_cast(unsigned long, Columns) + Left;
pD = dest[j];
for (unsigned long f = this->Frames; f != 0; --f)
{
pT = pCurrTemp = pTemp;
pS = pCurrSrc = pF;
// first, interpolate the columns:
// column 0, just copy the source data column 0
for (y = this->Src_Y; y != 0; --y)
{
*(pCurrTemp) = *(pCurrSrc);
pCurrSrc += Columns;
pCurrTemp += this->Dest_X;
}
pCurrSrc = pS;
// for the next few columns, linear interpolation
for (x = 1; x < xDelta + 1; ++x)
{
pCurrSrc = pS;
pCurrTemp = ++pT;
dOff = x * x_factor;
dOff = (1.0 < dOff) ? 1.0 : dOff;
for (y = this->Src_Y; y != 0; --y)
{
*(pCurrTemp) = OFstatic_cast(T, *(pCurrSrc) + (*(pCurrSrc + 1) - *(pCurrSrc)) * dOff);
pCurrSrc += Columns;
pCurrTemp += this->Dest_X;
}
}
nSrcIndex = 1;
pCurrSrc = ++pS;
// the majority of the columns
for (x = xDelta + 1; x < this->Dest_X - 2 * xDelta; ++x)
{
pCurrTemp = ++pT;
dOff = x * x_factor - nSrcIndex;
dOff = (1.0 < dOff) ? 1.0 : dOff;
for (y = this->Src_Y; y != 0; --y)
{
*(pCurrTemp) = OFstatic_cast(T, cubicValue(*(pCurrSrc - 1), *(pCurrSrc), *(pCurrSrc + 1), *(pCurrSrc + 2), dOff, minVal, maxVal));
pCurrSrc += Columns;
pCurrTemp += this->Dest_X;
}
// don't go beyond the source data
if ((nSrcIndex < this->Src_X - 3) && (x * x_factor >= nSrcIndex + 1))
{
pS++;
nSrcIndex++;
}
pCurrSrc = pS;
}
// last few columns except the very last one, linear interpolation
for (x = this->Dest_X - 2 * xDelta; x < this->Dest_X - 1; ++x)
{
pCurrTemp = ++pT;
dOff = x * x_factor - nSrcIndex;
dOff = (1.0 < dOff) ? 1.0 : dOff;
for (y = this->Src_Y; y != 0; --y)
{
*(pCurrTemp) = OFstatic_cast(T, *(pCurrSrc) + (*(pCurrSrc + 1) - *(pCurrSrc)) * dOff);
pCurrSrc += Columns;
pCurrTemp += this->Dest_X;
}
// don't go beyond the source data
if ((nSrcIndex < this->Src_X - 2) && (x * x_factor >= nSrcIndex + 1))
{
pS++;
nSrcIndex++;
}
pCurrSrc = pS;
}
// last column, just copy the source data column Src_X
pCurrTemp = pTemp + this->Dest_X - 1;
pCurrSrc = pF + this->Src_X - 1;
for (y = this->Src_Y; y != 0; --y)
{
*(pCurrTemp) = *(pCurrSrc);
pCurrSrc += Columns;
pCurrTemp += this->Dest_X;
}
// now the columns are interpolated in temp buffer, so interpolate the lines
pT = pCurrTemp = pTemp;
// line 0, just copy the temp buffer line 0
for (x = this->Dest_X; x != 0; --x)
*(pD++) = *(pCurrTemp++);
// for the next few lines, linear interpolation between line 0 and 1 of the temp buffer
for (y = 1; y < yDelta + 1; ++y)
{
pCurrTemp = pTemp;
dOff = y * y_factor;
dOff = (1.0 < dOff) ? 1.0 : dOff;
for (x = this->Dest_X; x != 0; --x)
{
*(pD++) = OFstatic_cast(T, *(pCurrTemp) + (*(pCurrTemp + this->Dest_X) - *(pCurrTemp)) * dOff);
pCurrTemp++;
}
}
nSrcIndex = 1;
pCurrTemp = pT = pTemp + this->Dest_X;
for (y = yDelta + 1; y < this->Dest_Y - yDelta - 1; ++y)
{
dOff = y * y_factor - nSrcIndex;
dOff = (1.0 < dOff) ? 1.0 : dOff;
for (x = this->Dest_X; x != 0; --x)
{
*(pD++) = OFstatic_cast(T, cubicValue(*(pCurrTemp - this->Dest_X),*(pCurrTemp), *(pCurrTemp + this->Dest_X),
*(pCurrTemp + this->Dest_X + this->Dest_X), dOff, minVal, maxVal));
pCurrTemp++;
}
// don't go beyond the source data
if ((nSrcIndex < this->Src_Y - 3) && (y * y_factor >= nSrcIndex + 1))
{
pT += this->Dest_X;
nSrcIndex++;
}
pCurrTemp = pT;
}
// the last few lines except the very last one, linear interpolation in between the second last and the last lines
pCurrTemp = pT = pTemp + OFstatic_cast(unsigned long, this->Src_Y - 2) * OFstatic_cast(unsigned long, this->Dest_X);
for (y = this->Dest_Y - yDelta - 1; y < this->Dest_Y - 1; ++y)
{
dOff = y * y_factor - nSrcIndex;
dOff = (1.0 < dOff) ? 1.0 : dOff;
for (x = this->Dest_X; x != 0; --x)
{
*(pD++) = OFstatic_cast(T, *(pCurrTemp) + (*(pCurrTemp + this->Dest_X) - *(pCurrTemp)) * dOff);
pCurrTemp++;
}
pCurrTemp = pT;
}
// the the last line, just copy the temp buffer line Src_X
pCurrTemp = pTemp + l_offset;
for (x = this->Dest_X; x != 0; --x)
*(pD++) = *(pCurrTemp++);
// skip to next frame
pF += f_size;
}
}
}
delete[] pTemp;
}
};
#endif