我正在为图像旋转问题而苦苦挣扎。我在以下链接中使用“按区域旋转旋转”的想法:
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;
                }
            }                
        }            
    } 




评论

解决主要问题后,如果要获得更好的图像质量,则应尝试使用双三次插值而不是双线性插值。 blog.demofox.org/2015/08/15/…

#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度旋转,底线仍然是空白,这使我发疯。我猜这是由于计算新图像的宽度和高度时计算不准确,但是我仍然无法弄清楚。