Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Matrix 二维照片的线性变换_Matrix_Algorithm - Fatal编程技术网

Matrix 二维照片的线性变换

Matrix 二维照片的线性变换,matrix,algorithm,Matrix,Algorithm,前言:我不确定这是一个数学问题还是一个编程问题,加上我是线性代数的noob,做这件事只是一个爱好。非常感谢您的帮助 假设我有一种任意的编程语言,它没有任何既定的机制来对位图图像(或任意的x,y值集合)执行线性变换。假设我想执行一些任意旋转、缩放和平移 现在,我将迭代每个x,y,得到像素颜色,并对其执行变换,四舍五入到最近的新x,y,将像素颜色值复制到,然后生成最终图像。它适用于我预先计算的简单旋转,但在我下面的TransformImage方法中,在i5上计算需要几秒钟,所以我想知道什么是更快的方

前言:我不确定这是一个数学问题还是一个编程问题,加上我是线性代数的noob,做这件事只是一个爱好。非常感谢您的帮助

假设我有一种任意的编程语言,它没有任何既定的机制来对位图图像(或任意的x,y值集合)执行线性变换。假设我想执行一些任意旋转、缩放和平移

现在,我将迭代每个x,y,得到像素颜色,并对其执行变换,四舍五入到最近的新x,y,将像素颜色值复制到,然后生成最终图像。它适用于我预先计算的简单旋转,但在我下面的TransformImage方法中,在i5上计算需要几秒钟,所以我想知道什么是更快的方法

这是我目前用C#测试的方法:

publicstaticcolor[,]TransformImage(Color[,]originalImage,float[,]transformationMatrix)
{
if(transformationMatrix.GetUpperBound(1)<1 | | transformationMatrix.GetUpperBound(0)<1)返回null;
int width=originalImage.GetUpperBound(1)+1;
int height=originalImage.GetUpperBound(0)+1;
颜色[,]新图像=新颜色[高度,宽度];

对于(int y=0;y基本上,这是一个您不希望自己用任意语言实现的操作。您需要考虑以下几个问题:

  • 所有边界检查都会带来一些费用。C#会检查所有数组索引的限制,你也会检查。对于每个像素。如果你幸运的话,JIT编译器会删除其中的一部分,但它仍然很昂贵。2D数组比更常见的1D数组更昂贵,尤其是在C#中
  • 浮点和整数之间的来回转换代价高昂。将整型转换为浮点四次,浮点转换为整型两次。每像素
  • 我们可以在这里停下来,只考虑每一个像素的操作次数,超出琐碎的任务。

    然而,更重要的是:

  • 内存不是统一的。您希望在一些连续的块中对其进行读写。您现在正以完美的顺序进行读取,但根据转换的不同,您正在到处写入。这很难针对“任意转换”进行优化,但总体思路是尝试制作适合缓存层次结构的小块。由于转换是线性的,因此一个空间中的任何小分片最终都将大致位于另一个空间中的类似位置
  • 现代CPU非常擅长矢量运算。通过适当的逻辑,每次可以复制32字节的块(8个32位像素),计算新像素位置也是如此
  • 然后,你会遇到如何处理别名的问题。没有简单的方法可以保证newImage中的每个像素都会映射到originalImage中的一个且仅映射到一个像素。你可能会多次写入同一个像素(你可能真的想要混合结果),而有些像素可能最终为空

    那么,在一段相当短的代码段中可以做些什么呢?不会太多。如果您想提高性能,我至少会尝试避开Math.Round(在进行粗略的舍入之前只需添加.5),理想情况下完全避免浮点运算。一个选项是存储整数乘数,然后移位(
    运算符)结果返回到相同的范围,即将
    cosAngle
    存储为
    cosAngle*65536
    并将
    newX
    移位16。根据前面的注释,甚至可能将转换矩阵存储为四个单独的浮点值比2D数组更可取。我希望JIT编译器能够处理这种情况

    <>但是,最终没有实现它的魔法。性能实现往往更久,也倾向于用其他语言编写。C或C++实现相同的逻辑可以加快一些速度,但是你仍然需要处理内存带宽问题,其中一个在整个源图像上的循环会给你。目标中对任意角度的不利内存访问模式。你可以自己测试的那部分,0的角度是否比,比如说,.4,快得多

    C实现的大部分加速将来自优化编译器的积极调整,以便为计算量大的循环发出整洁的代码,而.NET环境实际上更关注于业务线应用程序


    我不认为这里使用
    颜色
    类型是一个问题,因为.NET结构往往很快,但是如果您想评估性能,我可能会尝试使用一个简单的int或float灰度值数组来了解OO抽象是否以某种方式阻碍了适当的优化您不希望自己用任意语言实现的操作。您需要考虑以下几个问题:

  • 所有边界检查都会带来一些费用。C#会检查所有数组索引的限制,你也会检查。对于每个像素。如果你幸运的话,JIT编译器会删除其中的一部分,但它仍然很昂贵。2D数组比更常见的1D数组更昂贵,尤其是在C#中
  • 浮点和整数之间的来回转换代价高昂。将整型转换为浮点四次,浮点转换为整型两次。每像素
  • 我们可以在这里停下来,只考虑每一个像素的操作次数,超出琐碎的任务。

    然而,更重要的是:

  • 内存是不统一的。您希望在一些连续的块中对其进行读写。您现在正以完美的顺序进行读取,但取决于转换,您正在到处写入。对于“任意转换”,这很难进行优化,但是ge
            Color[,] BackupOriginal = OriginalColorData.Clone() as Color[,];
            float angle = 25.0f;
            float angleRadians = (float)(angle * (Math.PI / 180f));
            float cosAngle = (float)Math.Cos(angleRadians);
            float sinAngle = (float)Math.Sin(angleRadians);
            BackupOriginal = LinearTransformation.TransformImage(BackupOriginal, new float[2, 2] {
                {cosAngle,-1f * sinAngle},
                {sinAngle,cosAngle}
            });
    
        public static Color[,] TransformImage(Color[,] originalImage, float[,] transformationMatrix)
        {
            if (transformationMatrix.GetUpperBound(1) < 1 || transformationMatrix.GetUpperBound(0) < 1) return null;
    
            int width = originalImage.GetUpperBound(1) + 1;
            int height = originalImage.GetUpperBound(0) + 1;
            Color[,] newImage = new Color[height, width];
            for (int y=0;y<height;y++)
            {
                for (int x=0;x<width;x++)
                {
                    Color currentPixel = originalImage[y, x];
                    int newX = (int)Math.Round((x * transformationMatrix[0, 0]) + (y * transformationMatrix[0, 1]));
                    int newY = (int)Math.Round((x * transformationMatrix[1, 0]) + (y * transformationMatrix[1, 1]));
                    if (IsValidPixel(newX, newY, width, height))
                        newImage[newY, newX] = currentPixel;
                }
            }
            return newImage;
        }