雙線性插值(Bilinear interpolation)的圖像旋轉在mobile上面的C++實現

本文主要是介绍雙線性插值(Bilinear interpolation)的圖像旋轉在mobile上面的C++實現,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

http://blog.csdn.net/cay22/article/details/5555585 

雙線性插值(Bilinear interpolation)的圖像旋轉在mobile上面的C++實現

 

我們找來了圖像旋轉的公式:

 

X' =  X cosθ -  Y sinθ;

 

Y' =  X sinθ  + Y cosθ;

 

這個圖像公式大家在高中數學課都是會算滴。 然後我們要擴展一下因為我們不是在原點做旋轉,我們要圍繞原來的圖片中心做旋轉, 那麼我們假定原來的圖像中心是 oldCenterX, oldCenterY.旋轉完成以後, 我們要對圖像位置坐調整,調整到新的坐標中心, 那麼我們需要有個新的newCenterX, newCenterY;新的坐標就是新的圖片的中心。那麼我們的公式就可以轉化成了:

 

X' =  (X-oldCenterX) cosθ -  (Y-oldCenterY) sinθ  + newCenterX;

 

Y' =  (X-oldCenterX) sinθ  + (Y-oldCenterY) cosθ + newCenterY;

 

當然啦, 關鍵我們的問題不是旋轉後的位置,而是旋轉以後位置對於到原來的位置關系,也就是說我們更需要的是一個X,Y關於X'和Y'的表達式。很簡單的,我們把問題變成了2元一次方程!

 

X = Y'sinθ + X'cosθ + oldCenterY - newCenterX cosθ - newCenterY sinθ;

 

Y = Y'cosθ - X'sinθ + oldCenterY - newCenterY cosθ + newCenterX sinθ;

 

這樣要寫個合適的代碼就變得簡單了。 但是另一個顯著的問題就是沒有三角函數怎麼辦呢? 就像我們插值的時候用大數一樣, 我們用左移13位的大數來描述一下先,就像下面這樣的:

 

view plaincopy to clipboardprint?

//test interface for math  

const int K_CosineTable[24] =   

{  

    8192,  

    8172,  

    8112,  

    8012,  

    7874,  

    7697,  

    7483,  

    7233,  

    6947,  

    6627,  

    6275,  

    5892,  

    5481,  

    5043,  

    4580,  

    4096,  

    3591,  

    3068,  

    2531,  

    1981,  

    1422,  

    856,  

    285,  

    -285   

};  

int ShiftCos(int y)  

{  

    if (y<0) y*=-1;  

    y %= 360;  

    if ( y > 270 )   

    {  

        return ShiftCos((360 - y));  

    }  

    else if ( y > 180 )   

    {  

        return - ShiftCos((y - 180));  

    }  

    else if ( y > 90 )   

    {   

        return - ShiftCos((180 - y));  

    }  

    int index  = (y >> 2);  

    int offset = (y % 4);  

    // on the borderline of overflowing if use JInt16  

    int cosVal = (4 - offset) * K_CosineTable[index]  

    + offset * K_CosineTable[index + 1];  

    return cosVal >> 2;  

}  

int ShiftSin(int y)  

{  

    return ShiftCos(y + 270);  

有了這個三角函數的輔助:我們的最後的代碼就是這個樣子:

 

view plaincopy to clipboardprint?

/** 

** method to remove sharp the raw image with unsharp mask 

* @param src input grayscale binary array  

* @param srcWidth width of the input grayscale image 

* @param srcHeight height of the input grayscale image 

* @param [output] dst output gray-scale image. 

* @param [output] dstWidth width of the output grayscale image 

* @param [output] dstHeight height of the output grayscale image 

* @param angle, rotate angle. 

*/ 

void rotateImage (const unsigned char* src, int srcWidth, int srcHeight, unsigned char*& dst, int& dstWidth, int& dstHeight, int angle)  

{  

      

    // first calculate the new width and height;  

    const int SHIFT = 13;     

    dstWidth  = ( abs (srcWidth*ShiftCos(angle)) + abs (srcHeight*ShiftSin(angle))) >> SHIFT;           

    dstHeight = ( abs (srcWidth*ShiftSin(angle)) + abs (srcHeight*ShiftCos(angle))) >> SHIFT;           

    dst = new unsigned char [dstWidth*dstHeight];  

    int xcenter = srcWidth >> 1;  

    int ycenter = srcHeight >> 1;  

    int xnew = dstWidth >> 1;  

    int ynew = dstHeight >> 1;  

    const int xFix = ( xcenter <<8 ) - ((ynew * ShiftSin (angle)) >> 5 ) - ((xnew * ShiftCos (angle)) >> 5) ;    

    const int yFix = ( ycenter <<8 ) + ((xnew * ShiftSin (angle)) >> 5 ) - ((ynew * ShiftCos (angle)) >> 5) ;  

      

    int ox;  

    int oy;  

    int x;  

    int y;  

    int kx;  

    int ky;  

    int color [2][2];  

    for (int j=0;j<dstHeight;j++)  

    {  

        for (int i=0;i<dstWidth;i++)  

        {  

            ox = ((i * ShiftCos (angle) + j * ShiftSin (angle)) >> 5) + xFix;  

            oy = (((-1) * i * ShiftSin(angle) + j * ShiftCos (angle)) >> 5) + yFix;     

            if ( (ox >> 8) <= srcWidth && (ox >> 8) >=0 && (oy >> 8) <= srcHeight && (oy >> 8) >= 0)  

            {  

                kx = ox >> 8;  

                ky = oy >> 8;  

                x = ox & 0xFF;  

                y = oy & 0xFF;  

                color[0][0] = src[ ky*srcWidth + kx ];   

                color[1][0] = src[ ky*srcWidth + kx +1 ];   

                color[0][1] = src[ (ky+1)*srcWidth + kx ];   

                color[1][1] = src[ (ky+1)*srcWidth + kx+1 ];  

                int final = (0x100 - x)*(0x100 - y)*color[0][0] + x*(0x100 - y)*color[1][0] + (0x100-x)*y*color[0][1] + x*y*color[1][1];  

                final = final >> 16;  

                if (final>255)  

                    final = 255;  

                if (final<0)  

                    final = 0;  

                dst [ j*dstWidth + i] = (unsigned char)final;  

            }  

            else 

            {  

                dst [j*dstWidth + i] = 0xff;  

            }  

        }  

    }  

 

這裡說明一下的是接口的定義,這裡的和目標灰度圖相關的參數都是引用類型的。表示都是輸出的參數,因為圖像旋轉以後的大小會發生變化,函數外不是很方便事先分配好內存,所以這裡采用了就地分配的模式。內存分配在函數內部完成。雖然沒有用ticks去最後測速,但是想來沒有浮點數的計算,這裡的效率還是比較高的,當然這裡一些細節的記錄上還有可以再優化一下的,比如說這個常數5!!!Majic Number呵呵, 其實就是原來的那些數字都希望是左移8的, 所以三角函數中出來的數字需要左移5位!!除此以外就完全是公式的套用了 呵呵。

 

最後來點各個角度的效果圖看看:

 

20度

 

 

40度

 

 

 

60度

 

 

80度

 

 

100度

 

120度

 

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/hhygcy/archive/2009/08/20/4465876.aspx

这篇关于雙線性插值(Bilinear interpolation)的圖像旋轉在mobile上面的C++實現的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/192186

相关文章

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给

2024/9/8 c++ smart

1.通过自己编写的class来实现unique_ptr指针的功能 #include <iostream> using namespace std; template<class T> class unique_ptr { public:         //无参构造函数         unique_ptr();         //有参构造函数         unique_ptr(