2017年11月20日 星期一

[原始碼] C / C++ 旋轉 任意角度的圖片

[原始碼] C / C++ 如何旋轉圖片

首先會用到線性插補可以參考這一篇
https://charlottehong.blogspot.tw/2017/11/bilinear.html

旋轉公式

跟上一篇差不多意思線性插補指的是for迴圈去跑新圖,然後縮放對應回去原圖的座標,因為縮放得之後的點可能比較多或比較少,對應回去就會在非整數整數位置,這時候就看比例給數值。
旋轉也是一樣意思,旋轉過後的的點會正好不在點上,旋轉有兩個公式一般看到的就是給輸入原圖得出新圖的座標,這個不是我們要的,因為如果是這樣就不能插補了。
需要的是for去跑新圖座標,輸入新圖座標得到舊圖的座標(非整數),然後再利用線性插補補出來。
公式如下:
float r_rot = (j)*sin_t + (i)*cos_t; // 原圖X座標
float c_rot = (j)*cos_t - (i)*sin_t; // 原圖Y座標
其中 (j, i) 就是新圖的 (y, x) 座標,sin_t 與 cos_t 則是預先算好的數值,因為計算三角函數很費時,建議提出到for迴圈之外先算好,如果再for回圈內就要不斷的重複運算同一個角度浪費效能。
// 預算
float cos_t = cosf(sita*(float)(M_PI/180));
float sin_t = sinf(sita*(float)(M_PI/180));

原點在哪裡

需要注意的是上圖的公式會以圖的左上角 (0, 0) 作為定點轉換,如果需要從中心點旋轉或是任點轉需要轉換一下座標
假如現在要以 (10, 10) 為中心旋轉,那麼在旋轉前你需要先把for迴圈扣調這個範圍,也就是從 (j, i) 要從負號 -10 開始跑,經過旋轉公式計算出來之後還需要將原本的 10 加回來,這樣就可以獲得正確的位置了。
此外獲得正確位置之後還有一個問題就是旋轉後的圖比旋轉前的圖還要大,勢必會計算出超出原圖範圍的點,跑一個if過濾負號與超出原圖長寬的點即可。

範例代碼

只有給主要函式,有部分是已經寫好的函式庫,我有空再補上完整能跑的
(懶懶的~還沒補上又真的有需要然後看不懂可以直接留言問吧~我會看到的)
ImgRaw rotateImg(const ImgRaw& sou, size_t x, size_t y, float radius, float sita) {
    ImgRaw test = sou;
    //test.at2d(x, y) = 1;
    Draw::draw_arrow(test, y, x, radius, sita, 1);
    test.bmp("rotate_test.bmp");
    // 新圖長寬半徑
    float maxRadius = radius;
    int rotH = floor(maxRadius);
    int rotW = floor(maxRadius);
    ImgRaw rotate(rotW*2, rotH*2, 8);
    // 預算
    sita *= -1; // 把新圖轉回0度
    float cos_t = cosf(sita*(float)(M_PI/180));  
    float sin_t = sinf(sita*(float)(M_PI/180));
    // 跑新圖
    for (int j = -rotH; j < rotH; j++) {
        for (int i = -rotW; i < rotW; i++) {
            // 輸入新圖座標返回舊圖座標(已0, 0為圓心旋轉)
            float r_rot = (j)*sin_t + (i)*cos_t; // 原圖X座標
            float c_rot = (j)*cos_t - (i)*sin_t; // 原圖Y座標
            // 矯正回指定位置
            float rbin = r_rot + x; 
            float cbin = c_rot + y;
            // 去除原圖外的點
            if (cbin < sou.height - 1 and cbin > 0) {
                if (rbin < sou.width - 1 and rbin > 0) {
                    // 雙線姓插補
                    rotate.at2d(j+rotH, i+rotW) = test.atBilinear(cbin, rbin); 
                }
            }
        }
    }
    return rotate;
}
ImgRaw 帶有圖像的
  1. 一維陣列
  2. 長寬
  3. 位元數(彩圖灰圖)
  4. 一些方法
線性插補建議寫成一個讀取的,這樣規劃結構還不錯用 atBilinear(y, x) 要是你輸入的點不正好在點上,比如說 (0.5, 0.5) 就算出這個位置的插補值回傳。
這個函式的功能主要為輸入
  1. 點(y, x)
  2. 角度
  3. 半徑
輸出為
  1. 畫出點與半徑的箭頭
  2. 把點與角度轉正向右
  3. 擷取點為中心半徑的距離
其實就是SIFT算法的一小部分,轉正主角度時用到到的

2017年11月19日 星期日

sim卡轉接卡 空卡插進去拔不出來 卡住

sim卡轉接卡 空卡插進去拔不出來 卡住

今天在測試舊手機Z1的時候為了避免弄丟轉卡,不小心把空轉卡插進去了,然後就拔不出來了!
拿了保特瓶切了一些下來試著把她弄出來都弄不出來,後來想到說,他可以退出一點點,那我把方向對準把sim卡從那個縫隙塞進去不就好了
對準塞進去之後就可以正常抽出來了,結束了這場鬧劇QuQ

轉卡放在老家,下次有回去再補圖,總而言就是卡座拉一點點出來然後sim卡從那個縫隙塞進去就可以了,塞的時候方向要對準。

2017年11月16日 星期四

[原始碼] C / C++ 線性插補 bilinear 與注意事項

[原始碼] C / C++ 線性插補 bilinear 與注意事項

linear 意思就是依照比例去補數值,可以參考維基百科也說得很詳細
https://www.wikiwand.com/en/Bilinear_interpolation
簡單來說就是現在有兩個點 A=0 與 B=10
現在 A 的座標是 0,B的座標1,那個我要取出AB這個位置的點該怎麼辦呢
就是依照比例取因為比較靠近A所以分到的A比較多,離B比較遠分到的就比較少
現在讓我們來計算算法如下
AB = A*dx2 + B*dx1
大致上就這樣而已,如果是二維空間那個就要算三次這個公式意思如下
有ABCD四個點,把他當成2條一維就是AB跟CD,分別先算出他們中間的點
然後再把AB跟CD這兩個點在做一次運算就可以算出中間那個點了
float AB = A*dx2 + B*dx1;
float CD = C*dx2 + D*dx1;
float X  = AB*dy2 + CD*dy1;
linear是一維的意思,bilinear就是二維的意思,在上去還有三維都是差不多意思推倒

實作代碼

實作的時候要注意幾點

獲取鄰點

獲取鄰近點的時候不能用+1或-1,假設現在的點的 0 和 1,需要獲取的點是 1,先使用 floor 無條件捨去獲得1在加1就變成2了;正確的來說因為正好就落在點上,就直接把AB都帶那個點就可以了算出來是正確的
使用 ceil() 這樣就可以正確獲得了

獲取比例

這時候也是一樣,不過情況倒是反過來了,一定要用1去減,否則如果出現兩個一樣的點,比例兩邊都是0。
/*****************************************************************
Name : 
Date : 2017/11/16
By   : CharlotteHonG
Final: 2017/11/16
*****************************************************************/
#include <iostream>
#include <cmath>
using namespace std;

float linear(int* arr, float pos){
    // 獲取鄰點(不能用 1+)
    size_t c0 = floor(pos);
    size_t c1 = ceil(pos);
    // 獲取比例(只能用 1-)
    float dx1 = pos - c0;
    float dx2 = 1 - dx1;
    // 乘出比例(要交叉)
    float X = arr[c0]*dx2 + arr[c1]*dx1;
    return X;
}
float bilinear(int* arr, size_t w, float y, float x){
    // 獲取鄰點(不能用 1+)
    size_t x0 = floor(x);
    size_t x1 = ceil(x);
    size_t y0 = floor(y);
    size_t y1 = ceil(y);
    // 獲取比例(只能用 1-)
    float dx1 = x - x0;
    float dx2 = 1 - dx1;
    float dy1 = y - y0;
    float dy2 = 1 - dy1;
    // 獲取點
    const float& A = arr[y0*w + x0];
    const float& B = arr[y0*w + x1];
    const float& C = arr[y1*w + x0];
    const float& D = arr[y1*w + x1];
    // 乘出比例(要交叉)
    float AB = A*dx2 + B*dx1;
    float CD = C*dx2 + D*dx1;
    float X = AB*dy2 + CD*dy1;
    return X;
}
//================================================================
int main(int argc, char const *argv[]){
    int arr[] = {0, 10};
    cout << linear(arr, 0.1f) << endl;
    int arr2[] = {0, 0, 10, 10};
    cout << bilinear(arr2, 2, 0.1f, 0.1f) << endl;
    return 0;
}
//================================================================

縮放比例

縮小比例的時候,要注意新圖的點要向中間對齊,不然最右邊那一整排跟最下面一種排會沒有被計算到。
srcX = (dstX+0.5) * (srcW/dstW) - 0.5
srcY = (dstY+0.5) * (srcH/dstH) - 0.5
放大比例的時候則是要注意最外圍的一圈正好要疊在點上面
srcY = ((j)/ ((newH-1.f) / (height-1.f)) );
srcX = ((i)/ ((newW-1.f) / (width-1.f)) );
另外上述算是為了容易表達沒有加上修飾,要注意除法的時候如果是
(int) / (int);
返回的數值可能會出問題,至少讓其中一個是float或double才可以獲得正確結果。
然後連續除法兩次另一個除法也可以優化成乘法,效率會好一咪咪咪咪。
srcY = j * (height-1.f) / (newH-1.f);
srcX = i * (width-1.f)  / (newW-1.f);
這裡用到一個小技巧是 width-1.f 後面那一個是float所以在呼叫 operator-(float, float) 的時候會呼叫兩者都是float的operator,就幫你隱式轉型了。

2017年11月14日 星期二

微信支付只綁定台灣信用卡,使用權限與金額上限

微信支付只綁定台灣信用卡,使用權限與金額上限

詳細的開通使用方法請參考:
[圖文] 2017 台灣使用微信支付 不用別人給紅包

法規

2016年的時候大陸發布了新辦法,必須實名驗證才能使用
  1. 未实名认证 单笔:最高1000元, 每日:最高1000元, 每月:最高2000元
  2. 已实名认证 每日:最高10000元
官方連結:騰訊客服
實名驗證可以透過綁定台灣的信用卡取得


使用範圍

測試依據:2017.10站長大陸旅遊實測
微信支付只綁定台灣信用卡能夠到大陸支付嗎?
答案是部分可以的,支付有分兩種,一種是你掃對方的QR碼然後支付,這種的有出租車線上網購等等,單綁台灣信用卡即可支付使用,需要注意上限額度就好。
另一種是對方掃你的QR碼,例如大陸境內百貨公司,這個只有單綁台灣信用卡是不能支付的,推測綁定大陸銀行卡才行。
另一種比較特別的台灣境內的超商也有支持微信支付,這種支付除了要大陸銀行卡之外還要通過大陸身分證驗證。可以去全家掃掃看會傳簡訊跟你說要過身分證驗證XD

不能在百貨公司支付怎麼辦?

支付寶已經開放給台灣使用了,只要有台胞證即可,可以參考這篇站內文
台灣 支付寶 實名驗證圖文步驟
這個就無敵了,權限幾乎等同於大陸了,在百貨公司實測給店家掃碼可以支付;只差不能在台灣支付,一樣需要大陸身分證驗證。

2017年11月13日 星期一

C / C++ 函式傳遞二維陣列

C / C++ 函式傳遞二維陣列

二維的傳遞

一般來說我們嘗試傳著一個一維陣列是這樣傳遞的
void fun(int* p){...}

int arr[10]={};
fun(arr);
這種時候確實可行,編譯器會自動將型態 int(*)[10] 轉成 int* 然後成功的輸入。
要注意的是上面兩種型態是不同的,而且型態名稱就如上面上面那樣,一維的時候會自動轉換可是到了二維的時候就出問題,主要是因為編譯器只能轉一維
類類似於以下的代碼
int arr[][]={{0,1},{2,3}};
這是一個錯誤的代碼沒辦法辦法被執行,正確如下
int arr[][2]={{0,1},{2,3}};
你可以想像成編譯器只幫你處理最左邊的 [] 這裡會幫你補
所以一個二維的傳遞方式可以改成這樣
void fun(int (*p)[2]){...}
這樣就可以正常傳遞了,但是會限制住第二維的寬度另外他也相等於以下的寫法
void fun(int p[][2]){...}
這樣寫可能比較好理解

不定長度的二維傳遞

那如果要傳遞不定長的二維就必須使用 int** 了,不過這樣的用法沒辦法直接從int(*)[n] 轉型,你必須手動轉型
如果想要自動轉型成 int** 反推一下就要使用 int* 的陣列來轉型,這裡可能不好理解,就是星星兩邊各加1了。
int* 的陣列只的是一個陣列裡面放著一堆int*
int 的陣列只的是一個陣列裡面放著一堆int
二陣列的意思,可以想像成有一個一維陣列裡面放著一堆一維陣列
int arr1[2][2]{{1,2},{3,4}};

int* p1[2];
p1[0]=arr1[0];
p1[1]=arr1[1];
現在p1可以自動轉換成 int** 了

如何傳遞陣列的長寬

這是轉成指標之後的缺點,他將會遺失陣列的長度資訊,一般處理辦法就是函式多一個參數傳入,或者是拿陣列的第一個數值來當作長度。
C++的樣板可以解決這個問題,寫法如下
template<size_t N, size_t N2>
void priarr(int (&arr)[N][N2]) {};
如此一來就可以完整的傳入鎮列了

2017年11月12日 星期日

Visual Studio 2017 winres.h atlbase.h 找不到

Visual Studio 2017 winres.h atlbase.h 找不到

更新之後出現了
Cannot open include file: 'atlstr.h' Cannot open include file: 'winres.h'
解決辦法重新安裝相關套件
  • 安裝 ATL支援,就可以有 atlstr.h 了
  • WindoiwsSDK 上面也有更新版的,那個版本沒有winres.h的樣子

Android SONY 手機 Z、XZ系列 OTG 接上後毫無反應

 Android SONY 手機  Z系列 OTG 接上後沒有反應
===

## 設定方法
其實只是因為新版本的系統改成要你同意之後才偵測的到

到 `設定內` -> `裝置連線` -> 下方 `USB連線` -> `偵測USB裝置`

就可以偵測到 OTG 的裝置了。

## 其他
主要是因為後來的防水機都沒有做防水蓋了,考量到下水的時候可能會導致USB短路燒毀零件才作成這個樣子的,至於與電腦連接因為是電腦端供電而不是手機端供電就沒問題可以直接接上去就好。

如果OTG是連接記憶卡或USB隨身碟,格式要注意超過32GB的話只接受 exFAT 格式不然會讀取不到,硬碟的話可能要注意供電是否足夠。