http://www.leptonica.com/rotation.html
我的c#代码如下。这个想法是首先找到旋转图像的尺寸,然后找到旋转图像中每个像素的灰度。为了找到每个像素的灰度,将像素坐标转换回原始图像中的坐标(称为原始像素)。原始像素具有分数坐标值,然后使用链接中的双线性插值方法获得旋转图像中像素的灰度级。我的代码完成了大部分工作,除了在旋转的图像的角落有一些剪裁。我将旋转后的图像放到16 * 16的图像中,我们可以清楚地看到效果:旋转45度的底线没有灰度数据;在旋转90度后,左侧的垂直线没有灰度数据;并且在旋转180度后,左侧的垂直线和顶部的水平线都没有灰色。我相信这是由坐标计算引起的,我可以通过将一个坐标减去1以实现一定角度的正确旋转来修改代码,但在另一种情况下则无效。所以我想我的代码中一定有一些我忽略的东西。任何建议和评论都表示赞赏!
public void RotateImageClockwise(double degree)
{
Image<double> image = (Image<double>)(PreservedRawImage.Clone());
double sinAngle = Math.Sin(Math.PI * degree / 180.0);
double cosAngle = Math.Cos(Math.PI * degree / 180.0);
double newHeightRaw, newWidthRaw;
////Find the new dimensions of the rotated image.
if (Math.Abs(degree) <= 90)
{
newHeightRaw = image.Width * sinAngle + image.Height * cosAngle;
newWidthRaw = image.Width * cosAngle + image.Height * sinAngle;
}
else if (Math.Abs(degree) <= 180)
{
newHeightRaw = image.Width * Math.Sin(Math.PI * (180 - degree) / 180.0) + image.Height * Math.Sin(Math.PI * (degree - 90) / 180.0);
newWidthRaw = image.Width * Math.Cos(Math.PI * (180 - degree) / 180.0) + image.Height * Math.Cos(Math.PI * (degree - 90) / 180.0);
}
else
throw new Exception("Rotation angle is not right.");
////Done with finding the new dimensions
double newWidthHalf = newWidthRaw / 2;
double newHeightHalf = newHeightRaw / 2;
int newWidth = (int)Math.Round(newWidthRaw);
int newHeight = (int)Math.Round(newHeightRaw);
Image<double> newImage = new Image<double>(newWidth, newHeight);
byte[] bitmap = new byte[newWidth * newHeight];
double newMax = double.MinValue, newMin = double.MaxValue;
for (int x = 0; x < newHeight; x++)
{
for (int y = 0; y < newWidth; y++)
{
////Transform the coordinates to be centered at the center of the image
double xt = x - newHeightHalf;
double yt = y - newWidthHalf;
double originalX = xt * cosAngle - yt * sinAngle + image.Height / 2.0;
double originalY = xt * sinAngle + yt * cosAngle + image.Width / 2.0;
double fractionX = Math.Abs(originalX - (int)Math.Round(originalX));
double fractionY = Math.Abs(originalY - (int)Math.Round(originalY));
////Find the neighbors of the pixel in the original image
IList<Point<int>> neighbors = new List<Point<int>> { new Point<int>{x = (int)Math.Round(originalX), y = (int)Math.Round(originalY), weight = (1 - fractionX) * (1 - fractionY)},
new Point<int>{x = (int)Math.Round(originalX), y = (int)Math.Round(originalY) + 1, weight = fractionX * (1 - fractionY)},
new Point<int>{x = (int)Math.Round(originalX) + 1, y = (int)Math.Round(originalY), weight = (1 - fractionX) * fractionY},
new Point<int>{x = (int)Math.Round(originalX) + 1, y = (int)Math.Round(originalY) + 1, weight = fractionX * fractionY}
};
bool hasData = false;
double newData = 0;
double weight = 0;
foreach (var neighbor in neighbors)
{
if (neighbor.x >= 0 && neighbor.x < image.Height
&& neighbor.y >= 0 && neighbor.y < image.Width
)
{
hasData = true;
newData += neighbor.weight * image.Data[(neighbor.x) * image.Width + neighbor.y];
weight += neighbor.weight;
}
}
if (hasData && weight != 0)
{
newData /= weight;
newImage.Data[x * newWidth + y] = newData;
}
}
}
}
#1 楼
对图像来说,这更像是一条漫长的评论,而不是答案。我乍一看似乎认为像素采样点位于整数值。相反,实际上样本位于此位置的偏移0.5像素处。
图像1:像素样本是否位于其整数坐标上?还是位于偏移0.5像素的位置?
#2 楼
感谢您的答复。根据joojaa的评论,我对代码进行了如下修改,public void RotateImageClockwise(double degree)
{
Image<double> image = (Image<double>)(PreservedRawImage.Clone());
double sinAngle = Math.Sin(Math.PI * degree / 180.0);
double cosAngle = Math.Cos(Math.PI * degree / 180.0);
double newHeightRaw, newWidthRaw;
if (Math.Abs(degree) <= 90)
{
newHeightRaw = image.Width * Math.Sin(Math.PI * (Math.Abs(degree)) / 180.0) + image.Height * Math.Cos(Math.PI * Math.Abs(degree) / 180.0);
newWidthRaw = image.Width * Math.Cos(Math.PI * Math.Abs(degree) / 180.0) + image.Height * Math.Sin(Math.PI * (Math.Abs(degree)) / 180.0);
}
else if (Math.Abs(degree) <= 180)
{
newHeightRaw = image.Width * Math.Sin(Math.PI * (180 - Math.Abs(degree)) / 180.0) + image.Height * Math.Sin(Math.PI * (Math.Abs(degree) - 90) / 180.0);
newWidthRaw = image.Width * Math.Cos(Math.PI * (180 - Math.Abs(degree)) / 180.0) + image.Height * Math.Cos(Math.PI * (Math.Abs(degree) - 90) / 180.0);
}
int newWidth = (int)Math.Round(newWidthRaw);
int newHeight = (int)Math.Round(newHeightRaw);
Image<double> newImage = new Image<double>(newWidth, newHeight);
for (int x = 0; x < newHeight; x++)
{
for (int y = 0; y < newWidth; y++)
{
double xt = x - (newHeightRaw - 1) / 2.0;
double yt = y - (newWidthRaw - 1) / 2.0;
double originalX = xt * cosAngle - yt * sinAngle + (image.Height - 1) / 2.0;
double originalY = xt * sinAngle + yt * cosAngle + (image.Width - 1) / 2.0;
double fractionX = Math.Abs(originalX - (int)Math.Round(originalX));
double fractionY = Math.Abs(originalY - (int)Math.Round(originalY));
IList<Point<int>> neighbors = new List<Point<int>> { new Point<int>{x = (int)Math.Round(originalX), y = (int)Math.Round(originalY), weight = (1 - fractionX) * (1 - fractionY)},
new Point<int>{x = (int)Math.Round(originalX), y = (int)Math.Round(originalY) + 1, weight = fractionX * (1 - fractionY)},
new Point<int>{x = (int)Math.Round(originalX) + 1, y = (int)Math.Round(originalY), weight = (1 - fractionX) * fractionY},
new Point<int>{x = (int)Math.Round(originalX) + 1, y = (int)Math.Round(originalY) + 1, weight = fractionX * fractionY}
};
bool hasData = false;
double newData = 0;
double weight = 0;
foreach (var neighbor in neighbors)
{
if (neighbor.x >= 0 && neighbor.x < image.Height
&& neighbor.y >= 0 && neighbor.y < image.Width
&& (neighbor.x) * image.Width + neighbor.y < m_preservedImageValidDataBitmap.Length && m_preservedImageValidDataBitmap[(neighbor.x) * image.Width + neighbor.y] == 'T')
{
hasData = true;
newData += neighbor.weight * image.Data[(neighbor.x) * image.Width + neighbor.y];
weight += neighbor.weight;
}
}
if (hasData && weight != 0)
{
newData /= weight;
newImage.Data[x * newWidth + y] = newData;
}
}
}
}
现在,它可以旋转90度和180度。但是对于45度旋转,底线仍然是空白,这使我发疯。我猜这是由于计算新图像的宽度和高度时计算不准确,但是我仍然无法弄清楚。
评论
解决主要问题后,如果要获得更好的图像质量,则应尝试使用双三次插值而不是双线性插值。 blog.demofox.org/2015/08/15/…