我正在使用GD库动态创建图像
但是当我使用imagerotate()函数旋转图像时
它工作正常,但它给图像带来了非常刺激性的粗糙边缘
旋转。
如图所示。
那么如何使旋转图像的这些边/边平滑?
答案 0 :(得分:6)
避免在旋转图像时获取Jaggies effect的一种方法是使用另一种方式对像素进行采样,而不仅仅是调整像素,例如使用Nearest-neighbor interpolation使边缘更平滑。你可以看到matlab代码示例:
im1 = imread('lena.jpg');imshow(im1);
[m,n,p]=size(im1);
thet = rand(1);
mm = m*sqrt(2);
nn = n*sqrt(2);
for t=1:mm
for s=1:nn
i = uint16((t-mm/2)*cos(thet)+(s-nn/2)*sin(thet)+m/2);
j = uint16(-(t-mm/2)*sin(thet)+(s-nn/2)*cos(thet)+n/2);
if i>0 && j>0 && i<=m && j<=n
im2(t,s,:)=im1(i,j,:);
end
end
end
figure;
imshow(im2);
取自(here)。基本上,这意味着在对原始图片中的像素进行采样时,我们对近像素进行采样并对其进行插值以获得目标像素值。这条路 你可以通过安装任何附加包来实现你想要的。
修改强>
我发现了一些我曾用Java编写的旧代码,其中包含几个采样算法的实现。这是代码:
最近邻采样器:
/**
* @pre (this!=null) && (this.pixels!=null)
* @post returns the sampled pixel of (x,y) by nearest neighbor sampling
*/
private Pixel sampleNearestNeighbor(double x, double y) {
int X = (int) Math.round(x);
int Y = (int) Math.round(y);
if (X >= 0 && Y >= 0 && X < this.pixels.length
&& Y < this.pixels[0].length)
// (X,Y) is within this.pixels' borders
return new Pixel(pixels[X][Y].getRGB());
else
return new Pixel(255, 255, 255);
// sample color will be default white
}
双线性采样器:
/**
* @pre (this!=null) && (this.pixels!=null)
* @post returns the sampled pixel of (x,y) by bilinear interpolation
*/
private Pixel sampleBilinear(double x, double y) {
int x1, y1, x2, y2;
x1 = (int) Math.floor(x);
y1 = (int) Math.floor(y);
double weightX = x - x1;
double weightY = y - y1;
if (x1 >= 0 && y1 >= 0 && x1 + 1 < this.pixels.length
&& y1 + 1 < this.pixels[0].length) {
x2 = x1 + 1;
y2 = y1 + 1;
double redAX = (weightX * this.pixels[x2][y1].getRed())
+ (1 - weightX) * this.pixels[x1][y1].getRed();
double greenAX = (weightX * this.pixels[x2][y1].getGreen())
+ (1 - weightX) * this.pixels[x1][y1].getGreen();
double blueAX = (weightX * this.pixels[x2][y1].getBlue())
+ (1 - weightX) * this.pixels[x1][y1].getBlue();
// bilinear interpolation of A point
double redBX = (weightX * this.pixels[x2][y2].getRed())
+ (1 - weightX) * this.pixels[x1][y2].getRed();
double greenBX = (weightX * this.pixels[x2][y2].getGreen())
+ (1 - weightX) * this.pixels[x1][y2].getGreen();
double blueBX = (weightX * this.pixels[x2][y2].getBlue())
+ (1 - weightX) * this.pixels[x1][y2].getBlue();
// bilinear interpolation of B point
int red = (int) (weightY * redBX + (1 - weightY) * redAX);
int green = (int) (weightY * greenBX + (1 - weightY) * greenAX);
int blue = (int) (weightY * blueBX + (1 - weightY) * blueAX);
// bilinear interpolation of A and B
return new Pixel(red, green, blue);
} else if (x1 >= 0
&& y1 >= 0 // last row or column
&& (x1 == this.pixels.length - 1 || y1 == this.pixels[0].length - 1)) {
return new Pixel(this.pixels[x1][y1].getRed(), this.pixels[x1][y1]
.getGreen(), this.pixels[x1][y1].getBlue());
} else
return new Pixel(255, 255, 255);
// sample color will be default white
}
高斯采样器:
/**
* @pre (this!=null) && (this.pixels!=null)
* @post returns the sampled pixel of (x,y) by gaussian function
*/
private Pixel sampleGaussian(double u, double v) {
double w = 3; // sampling distance
double sqrSigma = Math.pow(w / 3.0, 2); // sigma^2
double normal = 0;
double red = 0, green = 0, blue = 0;
double minIX = Math.round(u - w);
double maxIX = Math.round(u + w);
double minIY = Math.round(v - w);
double maxIY = Math.round(v + w);
for (int ix = (int) minIX; ix <= maxIX; ix++) {
for (int iy = (int) minIY; iy <= maxIY; iy++) {
double sqrD = Math.pow(ix - u, 2) + Math.pow(iy - v, 2);
// squared distance between (ix,iy) and (u,v)
if (sqrD < Math.pow(w, 2) && ix >= 0 && iy >= 0
&& ix < pixels.length && iy < pixels[0].length) {
// gaussian function
double gaussianWeight = Math.pow(2, -1 * (sqrD / sqrSigma));
normal += gaussianWeight;
red += gaussianWeight * pixels[ix][iy].getRed();
green += gaussianWeight * pixels[ix][iy].getGreen();
blue += gaussianWeight * pixels[ix][iy].getBlue();
}
}
}
red /= normal;
green /= normal;
blue /= normal;
return new Pixel(red, green, blue);
}
实际轮播:
/**
* @pre (this!=null) && (this.pixels!=null) && (1 <= samplingMethod <= 3)
* @post creates a new rotated-by-degrees Image and returns it
*/
public myImage rotate(double degrees, int samplingMethod) {
myImage outputImg = null;
int t = 0;
for (; degrees < 0 || degrees >= 180; degrees += (degrees < 0) ? 180
: -180)
t++;
int w = this.pixels.length;
int h = this.pixels[0].length;
double cosinus = Math.cos(Math.toRadians(degrees));
double sinus = Math.sin(Math.toRadians(degrees));
int width = Math.round((float) (w * Math.abs(cosinus) + h * sinus));
int height = Math.round((float) (h * Math.abs(cosinus) + w * sinus));
w--;
h--; // move from (1,..,k) to (0,..,1-k)
Pixel[][] pixelsArray = new Pixel[width][height];
double x = 0; // x coordinate in the source image
double y = 0; // y coordinate in the source image
if (degrees >= 90) { // // 270 or 90 degrees turn
double temp = cosinus;
cosinus = sinus;
sinus = -temp;
}
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
double x0 = i;
double y0 = j;
if (degrees >= 90) {
if ((t % 2 == 1)) { // 270 degrees turn
x0 = j;
y0 = width - i - 1;
} else { // 90 degrees turn
x0 = height - j - 1;
y0 = i;
}
} else if (t % 2 == 1) { // 180 degrees turn
x0 = width - x0 - 1;
y0 = height - y0 - 1;
}
// calculate new x/y coordinates and
// adjust their locations to the middle of the picture
x = x0 * cosinus - (y0 - sinus * w) * sinus;
y = x0 * sinus + (y0 - sinus * w) * cosinus;
if (x < -0.5 || x > w + 0.5 || y < -0.5 || y > h + 0.5)
// the pixels that does not have a source will be painted in
// default white
pixelsArray[i][j] = new Pixel(255, 255, 255);
else {
if (samplingMethod == 1)
pixelsArray[i][j] = sampleNearestNeighbor(x, y);
else if (samplingMethod == 2)
pixelsArray[i][j] = sampleBilinear(x, y);
else if (samplingMethod == 3)
pixelsArray[i][j] = sampleGaussian(x, y);
}
}
outputImg = new myImage(pixelsArray);
}
return outputImg;
}
答案 1 :(得分:2)
这可能听起来相当黑客,但这是最简单的方法,甚至大型企业解决方案都使用它。
诀窍是首先创建所需大小的2X图像,然后进行所有绘图调用,然后将其调整为所需的原始大小。
它不仅非常容易实现,而且它的速度也很快,并且可以产生非常好的效果。当我需要对边缘应用模糊时,我会在所有情况下使用此技巧。
另一个优点是它不会在图像的其余部分包含模糊,并且保持清晰 - 只有旋转图像的边框才能变得平滑。
答案 2 :(得分:0)
您可以尝试的一件事是使用imageantialias()
来平滑边缘。
如果这不符合您的需求,GD本身可能就不够了。
GD使用非常快速的方法来实现所有它的实际平滑或任何相关的东西。如果你想要一些正确的图像编辑,你可以查看ImageMagick(这需要服务器上的额外软件)或者根据GD编写你自己的函数。
请记住,PHP的数据量非常大,因此编写自己的函数可能会令人失望。 (根据我的经验,PHP比编译代码慢大约40倍。)
我建议将ImageMagick用于结果质量很重要的任何图像处理。