📄 ximadsp.cpp
字号:
if (a) {
a->Resample(w,h);
#if CXIMAGE_SUPPORT_ALPHA
AlphaCreate();
#endif //CXIMAGE_SUPPORT_ALPHA
}
RGBQUAD c;
for (long y=0;y<h;y++){
info.nProgress = (long)(100*y/h); //<Anatoly Ivasyuk>
for (long x=0;x<w;x++){
c.rgbRed=r->GetPixelIndex(x,y);
c.rgbGreen=g->GetPixelIndex(x,y);
c.rgbBlue=b->GetPixelIndex(x,y);
switch (colorspace){
case 1:
BlindSetPixelColor(x,y,HSLtoRGB(c));
break;
case 2:
BlindSetPixelColor(x,y,YUVtoRGB(c));
break;
case 3:
BlindSetPixelColor(x,y,YIQtoRGB(c));
break;
case 4:
BlindSetPixelColor(x,y,XYZtoRGB(c));
break;
default:
BlindSetPixelColor(x,y,c);
}
#if CXIMAGE_SUPPORT_ALPHA
if (a) AlphaSet(x,y,a->GetPixelIndex(x,y));
#endif //CXIMAGE_SUPPORT_ALPHA
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
/**
* Smart blurring to remove small defects, dithering or artifacts.
* \param radius: normally between 0.01 and 0.5
* \param niterations: should be trimmed with radius, to avoid blurring should be (radius*niterations)<1
* \param colorspace: 0 = RGB, 1 = HSL, 2 = YUV, 3 = YIQ, 4 = XYZ
* \return true if everything is ok
*/
bool CxImage::Repair(float radius, long niterations, long colorspace)
{
if (!IsValid()) return false;
long w = GetWidth();
long h = GetHeight();
CxImage r,g,b;
r.Create(w,h,8);
g.Create(w,h,8);
b.Create(w,h,8);
switch (colorspace){
case 1:
SplitHSL(&r,&g,&b);
break;
case 2:
SplitYUV(&r,&g,&b);
break;
case 3:
SplitYIQ(&r,&g,&b);
break;
case 4:
SplitXYZ(&r,&g,&b);
break;
default:
SplitRGB(&r,&g,&b);
}
for (int i=0; i<niterations; i++){
RepairChannel(&r,radius);
RepairChannel(&g,radius);
RepairChannel(&b,radius);
}
CxImage* a=NULL;
#if CXIMAGE_SUPPORT_ALPHA
if (AlphaIsValid()){
a = new CxImage();
AlphaSplit(a);
}
#endif
Combine(&r,&g,&b,a,colorspace);
delete a;
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool CxImage::RepairChannel(CxImage *ch, float radius)
{
if (ch==NULL) return false;
CxImage tmp(*ch);
if (!tmp.IsValid()){
strcpy(info.szLastError,tmp.GetLastError());
return false;
}
long w = ch->GetWidth()-1;
long h = ch->GetHeight()-1;
double correction,ix,iy,ixx,ixy,iyy;
int x,y,xy0,xp1,xm1,yp1,ym1;
for(x=1; x<w; x++){
for(y=1; y<h; y++){
xy0 = ch->BlindGetPixelIndex(x,y);
xm1 = ch->BlindGetPixelIndex(x-1,y);
xp1 = ch->BlindGetPixelIndex(x+1,y);
ym1 = ch->BlindGetPixelIndex(x,y-1);
yp1 = ch->BlindGetPixelIndex(x,y+1);
ix= (xp1-xm1)/2.0;
iy= (yp1-ym1)/2.0;
ixx= xp1 - 2.0 * xy0 + xm1;
iyy= yp1 - 2.0 * xy0 + ym1;
ixy=(ch->BlindGetPixelIndex(x+1,y+1) + ch->BlindGetPixelIndex(x-1,y-1) -
ch->BlindGetPixelIndex(x-1,y+1) - ch->BlindGetPixelIndex(x+1,y-1))/4.0;
correction = ((1.0+iy*iy)*ixx - ix*iy*ixy + (1.0+ix*ix)*iyy)/(1.0+ix*ix+iy*iy);
tmp.BlindSetPixelIndex(x,y,(BYTE)min(255,max(0,(xy0 + radius * correction + 0.5))));
}
}
for (x=0;x<=w;x++){
for(y=0; y<=h; y+=h){
xy0 = ch->BlindGetPixelIndex(x,y);
xm1 = ch->GetPixelIndex(x-1,y);
xp1 = ch->GetPixelIndex(x+1,y);
ym1 = ch->GetPixelIndex(x,y-1);
yp1 = ch->GetPixelIndex(x,y+1);
ix= (xp1-xm1)/2.0;
iy= (yp1-ym1)/2.0;
ixx= xp1 - 2.0 * xy0 + xm1;
iyy= yp1 - 2.0 * xy0 + ym1;
ixy=(ch->GetPixelIndex(x+1,y+1) + ch->GetPixelIndex(x-1,y-1) -
ch->GetPixelIndex(x-1,y+1) - ch->GetPixelIndex(x+1,y-1))/4.0;
correction = ((1.0+iy*iy)*ixx - ix*iy*ixy + (1.0+ix*ix)*iyy)/(1.0+ix*ix+iy*iy);
tmp.BlindSetPixelIndex(x,y,(BYTE)min(255,max(0,(xy0 + radius * correction + 0.5))));
}
}
for (x=0;x<=w;x+=w){
for (y=0;y<=h;y++){
xy0 = ch->BlindGetPixelIndex(x,y);
xm1 = ch->GetPixelIndex(x-1,y);
xp1 = ch->GetPixelIndex(x+1,y);
ym1 = ch->GetPixelIndex(x,y-1);
yp1 = ch->GetPixelIndex(x,y+1);
ix= (xp1-xm1)/2.0;
iy= (yp1-ym1)/2.0;
ixx= xp1 - 2.0 * xy0 + xm1;
iyy= yp1 - 2.0 * xy0 + ym1;
ixy=(ch->GetPixelIndex(x+1,y+1) + ch->GetPixelIndex(x-1,y-1) -
ch->GetPixelIndex(x-1,y+1) - ch->GetPixelIndex(x+1,y-1))/4.0;
correction = ((1.0+iy*iy)*ixx - ix*iy*ixy + (1.0+ix*ix)*iyy)/(1.0+ix*ix+iy*iy);
tmp.BlindSetPixelIndex(x,y,(BYTE)min(255,max(0,(xy0 + radius * correction + 0.5))));
}
}
ch->Transfer(tmp);
return true;
}
////////////////////////////////////////////////////////////////////////////////
/**
* Enhance the variations between adjacent pixels.
* Similar results can be achieved using Filter(),
* but the algorithms are different both in Edge() and in Contour().
* \return true if everything is ok
*/
bool CxImage::Contour()
{
if (!pDib) return false;
long Ksize = 3;
long k2 = Ksize/2;
long kmax= Ksize-k2;
long i,j,k;
BYTE maxr,maxg,maxb;
RGBQUAD pix1,pix2;
CxImage tmp(*this);
if (!tmp.IsValid()){
strcpy(info.szLastError,tmp.GetLastError());
return false;
}
long xmin,xmax,ymin,ymax;
if (pSelection){
xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;
ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;
} else {
xmin = ymin = 0;
xmax = head.biWidth; ymax=head.biHeight;
}
for(long y=ymin; y<ymax; y++){
info.nProgress = (long)(100*(y-ymin)/(ymax-ymin));
if (info.nEscape) break;
for(long x=xmin; x<xmax; x++){
#if CXIMAGE_SUPPORT_SELECTION
if (BlindSelectionIsInside(x,y))
#endif //CXIMAGE_SUPPORT_SELECTION
{
pix1 = BlindGetPixelColor(x,y);
maxr=maxg=maxb=0;
for(j=-k2, i=0;j<kmax;j++){
for(k=-k2;k<kmax;k++, i++){
if (!IsInside(x+j,y+k)) continue;
pix2 = BlindGetPixelColor(x+j,y+k);
if ((pix2.rgbBlue-pix1.rgbBlue)>maxb) maxb = pix2.rgbBlue;
if ((pix2.rgbGreen-pix1.rgbGreen)>maxg) maxg = pix2.rgbGreen;
if ((pix2.rgbRed-pix1.rgbRed)>maxr) maxr = pix2.rgbRed;
}
}
pix1.rgbBlue=(BYTE)(255-maxb);
pix1.rgbGreen=(BYTE)(255-maxg);
pix1.rgbRed=(BYTE)(255-maxr);
tmp.BlindSetPixelColor(x,y,pix1);
}
}
}
Transfer(tmp);
return true;
}
////////////////////////////////////////////////////////////////////////////////
/**
* Adds a random offset to each pixel in the image
* \param radius: maximum pixel displacement
* \return true if everything is ok
*/
bool CxImage::Jitter(long radius)
{
if (!pDib) return false;
long nx,ny;
CxImage tmp(*this);
if (!tmp.IsValid()){
strcpy(info.szLastError,tmp.GetLastError());
return false;
}
long xmin,xmax,ymin,ymax;
if (pSelection){
xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;
ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;
} else {
xmin = ymin = 0;
xmax = head.biWidth; ymax=head.biHeight;
}
for(long y=ymin; y<ymax; y++){
info.nProgress = (long)(100*(y-ymin)/(ymax-ymin));
if (info.nEscape) break;
for(long x=xmin; x<xmax; x++){
#if CXIMAGE_SUPPORT_SELECTION
if (BlindSelectionIsInside(x,y))
#endif //CXIMAGE_SUPPORT_SELECTION
{
nx=x+(long)((rand()/(float)RAND_MAX - 0.5)*(radius*2));
ny=y+(long)((rand()/(float)RAND_MAX - 0.5)*(radius*2));
if (!IsInside(nx,ny)) {
nx=x;
ny=y;
}
if (head.biClrUsed==0){
tmp.BlindSetPixelColor(x,y,BlindGetPixelColor(nx,ny));
} else {
tmp.BlindSetPixelIndex(x,y,BlindGetPixelIndex(nx,ny));
}
#if CXIMAGE_SUPPORT_ALPHA
tmp.AlphaSet(x,y,AlphaGet(nx,ny));
#endif //CXIMAGE_SUPPORT_ALPHA
}
}
}
Transfer(tmp);
return true;
}
////////////////////////////////////////////////////////////////////////////////
/**
* generates a 1-D convolution matrix to be used for each pass of
* a two-pass gaussian blur. Returns the length of the matrix.
* \author [nipper]
*/
int CxImage::gen_convolve_matrix (float radius, float **cmatrix_p)
{
int matrix_length;
int matrix_midpoint;
float* cmatrix;
int i,j;
float std_dev;
float sum;
/* we want to generate a matrix that goes out a certain radius
* from the center, so we have to go out ceil(rad-0.5) pixels,
* inlcuding the center pixel. Of course, that's only in one direction,
* so we have to go the same amount in the other direction, but not count
* the center pixel again. So we double the previous result and subtract
* one.
* The radius parameter that is passed to this function is used as
* the standard deviation, and the radius of effect is the
* standard deviation * 2. It's a little confusing.
* <DP> modified scaling, so that matrix_lenght = 1+2*radius parameter
*/
radius = (float)fabs(0.5*radius) + 0.25f;
std_dev = radius;
radius = std_dev * 2;
/* go out 'radius' in each direction */
matrix_length = int (2 * ceil(radius-0.5) + 1);
if (matrix_length <= 0) matrix_length = 1;
matrix_midpoint = matrix_length/2 + 1;
*cmatrix_p = new float[matrix_length];
cmatrix = *cmatrix_p;
/* Now we fill the matrix by doing a numeric integration approximation
* from -2*std_dev to 2*std_dev, sampling 50 points per pixel.
* We do the bottom half, mirror it to the top half, then compute the
* center point. Otherwise asymmetric quantization errors will occur.
* The formula to integrate is e^-(x^2/2s^2).
*/
/* first we do the top (right) half of matrix */
for (i = matrix_length/2 + 1; i < matrix_length; i++)
{
float base_x = i - (float)floor((float)(matrix_length/2)) - 0.5f;
sum = 0;
for (j = 1; j <= 50; j++)
{
if ( base_x+0.02*j <= radius )
sum += (float)exp (-(base_x+0.02*j)*(base_x+0.02*j) /
(2*std_dev*std_dev));
}
cmatrix[i] = sum/50;
}
/* mirror the thing to the bottom half */
for (i=0; i<=matrix_length/2; i++) {
cmatrix[i] = cmatrix[matrix_length-1-i];
}
/* find center val -- calculate an odd number of quanta to make it symmetric,
* even if the center point is weighted slightly higher than others. */
sum = 0;
for (j=0; j<=50; j++)
{
sum += (float)exp (-(0.5+0.02*j)*(0.5+0.02*j) /
(2*std_dev*std_dev));
}
cmatrix[matrix_length/2] = sum/51;
/* normalize the distribution by scaling the total sum to one */
sum=0;
for (i=0; i<matrix_length; i++) sum += cmatrix[i];
for (i=0; i<matrix_length; i++) cmatrix[i] = cmatrix[i] / sum;
return matrix_length;
}
////////////////////////////////////////////////////////////////////////////////
/**
* generates a lookup table for every possible product of 0-255 and
* each value in the convolution matrix. The returned array is
* indexed first by matrix position, then by input multiplicand (?)
* value.
* \author [nipper]
*/
float* CxImage::gen_lookup_table (float *cmatrix, int cmatrix_length)
{
float* lookup_table = new float[cmatrix_length * 256];
float* lookup_table_p = lookup_table;
float* cmatrix_p = cmatrix;
for (int i=0; i<cmatrix_length; i++)
{
for (int j=0; j<256; j++)
{
*(lookup_table_p++) = *cmatrix_p * (float)j;
}
cmatrix_p++;
}
return lookup_table;
}
////////////////////////////////////////////////////////////////////////////////
/**
* this function is written as if it is blurring a column at a time,
* even though it can operate on rows, too. There is no difference
* in the processing of the lines, at least to the blur_line function.
* \author [nipper]
*/
void CxImage::blur_line (float *ctable, float *cmatrix, int cmatrix_length, BYTE* cur_col, BYTE* dest_col, int y, long bytes)
{
float scale;
float sum;
int i=0, j=0;
int row;
int cmatrix_middle = cmatrix_length/2;
float *cmatrix_p;
BYTE *cur_col_p;
BYTE *cur_col_p1;
BYTE *dest_col_p;
float *ctable_p;
/* this first block is the same as the non-optimized version --
* it is only used for very small pictures, so speed isn't a
* big concern.
*/
if (cmatrix_length > y)
{
for (row = 0; row < y ; row++)
{
scale=0;
/* find the scale factor */
for (j = 0; j < y ; j++)
{
/* if the index is in bounds, add it to the scale counter */
if ((j + cmatrix_middle - row >= 0) &&
(j + cmatrix_middle - row < cmatrix_length))
scale += cmatrix[j + cmatrix_middle - row];
}
for (i = 0; i<bytes; i++)
{
sum = 0;
for (j = 0; j < y;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -