2017年10月4日 星期三

C / C++ 影像處理如何畫線與畫箭頭

C++ 影像處理如何畫線與畫箭頭

代碼關鍵函式要用點斜式,然後用for迴圈去跑x軸或y軸,跑完那一條線
struct ImgRaw{
    int width;
    int height;
    vector<unsigned char> raw_img;
};

void drawLine_p(ImgRaw& img, int y, int x, int y2, int x2, float val) {
    // 兩點之間的距離差
    float dx = static_cast<float>(x2-x);
    float dy = static_cast<float>(y2-y);
    // 以Y軸為主
    float sita=fastAtan2f(dy, dx);
    if (sita>45 and sita<135 or sita>225 and sita<315) {
        float slopeY = dx/dy; // 斜率
        for (int i = 0; i < abs(dy); i++) {
            int iFix = dy>0? i:-i;
            int currPos = static_cast<int>(iFix*slopeY+.5f + x);

            int distX = currPos;
            int distY = y+iFix;

            if (distX<0 or distX>=(int)img.width or distY<0 or distY>=(int)img.height) {
                return;
            }
            img.raw_img[distY*img.width + distX] = static_cast<unsigned char>(val);
        }
    } 
    // 以X軸為主
    else {
        float slopeX = dy/dx; // 斜率
        for (int i = 0; i < abs(dx); i++) {
            int iFix = dx>0? i:-i;
            int currPos = static_cast<int>(iFix*slopeX+.5 + y);

            int distX = x+iFix;
            int distY = currPos;

            if (distX<0 or distX>=(int)img.width or distY<0 or distY>=(int)img.height) {
                return;
            }
            img.raw_img[distY*img.width + distX] = (unsigned char)val;
        }
    }
}
然後角度的不一樣比如說45~90度跑Y軸會比較好,線連得比較密,如果是0~45度則是跑X軸比較好,才不會出現斷線。
角度的判斷計算使用。
float sita = fastAtan2f(dy, dx);
  • ImgRaw帶有圖像資訊、長、寬
  • fastAtan2f是從OpenCV複製出來的可以參考站內文這裡
接下來有了直線之後就是寫一個,輸入角度與長度的函式,簡單來說就是算出頭尾的位置然後帶入剛剛的函式畫線。
void drawLineRGB_s(ImgRaw& img, int y, int x, float line_len, float sg) {
        // 檢查
        if (line_len <= 0) { return; }
        // 算頭尾
        int x2 = x + line_len*cos(sg * M_PI/180);
        int y2 = y + line_len*sin(sg * M_PI/180);
        // 畫線
        drawLineRGB_p(img, y, x, y2, x2);
    }
再來箭頭就只是多話2條線,帶入剛剛的給點給角度的函式畫出箭頭
void draw_arrowRGB(ImgRaw& img, int y, int x, float line_len, float sg) {
        // 檢查
        if (line_len <= 0) { return; }
        // 算頭尾
        int x2 = x + line_len*cos(sg * M_PI/180);
        int y2 = y + line_len*sin(sg * M_PI/180);
        // 畫線
        drawLineRGB_p(img, y, x, y2, x2);
        // 畫頭
        drawLineRGB_s(img, y2, x2, 10, sg-150);
        drawLineRGB_s(img, y2, x2, 10, sg+150);
    }


如何使用

drawLine有兩個,一個是給定兩個點,把他們連起來,另一個是給定原點, 長度, 角度,也就是給極值的方式畫線。
    ImgRaw img_lineRGB(1280, 720);
    for (size_t i = 0; i < 36; i++) {
        Draw::draw_arrowRGB(img_lineRGB, 200, 200, 100.f*sqrt(2), i*10);
    } img_lineRGB.bmp("lineRGB.bmp");
ImgRaw 做的事情很簡單只是開一個 vector(1280*720*3) 然後儲存他的長1280和寬720
畫一圈的箭頭

原理

主要是利用斜率去算的,假設有兩個點,分別是 (0, 0) 與 (40,50) 斜率的公式,其中b是偏移量暫時不管她。
y=ax+b;
斜率就只是相減而已
dy=50-0;
dx=40-0;

m=dy/dx;
既然已知斜率那就用for迴圈去跑x的範圍 0~40 就可以得出y的位置了
不過這會遇到一個問題是當直線是上下的時候 for 迴圈跑的次數是0畫不出線,其次可以發現這種畫法當線條比較偏上的時候,45~135度的範圍會明顯斷線的感覺,因此這時候要變成以y為主,for迴圈去跑y反向計算出x。
這樣就可以畫出上圖比較好看一些的線條了。


範例程式

完整的程式參考:https://goo.gl/rAJLjH

atan() 和 atan2() 快速算法

atan() 計算的時候如何得知正確角度;fastAtan2f() 快速計算方法

atan() 在計算的時候是沒有把dx和dy的正負號考慮進去的,也就是說在第3象限的時候會被當作第一象限來處理。
解決方法很容易,有一個已經幫你處好的函式是 atan2() 可以直接輸入 atan2(dy, dx) 計算出正確的結果,不過出來的角度是 -PI~PI 逕度還需要處理一下轉為角度。
這裡提供一個已經處理好上述而且速度更快的方法,是來自於OpenCV內的函式原封不動複製出來的應該足夠安全。

atan2()

static inline float fastAtan2f(float dy, float dx){
    // 快速atan運算
    static const float atan2_p1 = 0.9997878412794807f*(float)(180/M_PI);
    static const float atan2_p3 = -0.3258083974640975f*(float)(180/M_PI);
    static const float atan2_p5 = 0.1555786518463281f*(float)(180/M_PI);
    static const float atan2_p7 = -0.04432655554792128f*(float)(180/M_PI);
    static const float atan2_DBL_EPSILON = 2.2204460492503131e-016;

    float ax = std::abs(dx), ay = std::abs(dy);
    float a, c, c2;
    if (ax >= ay) {
        c = ay/(ax + static_cast<float>(atan2_DBL_EPSILON));
        c2 = c*c;
        a = (((atan2_p7*c2 + atan2_p5)*c2 + atan2_p3)*c2 + atan2_p1)*c;
    } else {
        c = ax/(ay + static_cast<float>(atan2_DBL_EPSILON));
        c2 = c*c;
        a = 90.f - (((atan2_p7*c2 + atan2_p5)*c2 + atan2_p3)*c2 + atan2_p1)*c;
    }
    if (dx < 0)
        a = 180.f - a;
    if (dy < 0)
        a = 360.f - a;
    return a;
}
fastAtan2f() 計算出來就是 0~360 度的角度了,如果需要逕度從代碼內把轉換的移除即可。
M_PI 可能需要自己定義一下,或者引入cmath並啟用宏定義(啟用是一行宏定義的代碼需要再自己查)。
如果只是單單需要拍自己定一個比較快~
#define M_PI 3.14159265358979323846

atan()

這個OpenCV裡面就沒有,不過可以從上面的 atan2() 修改一下即可(簡單來說就是x設定成1)。
這個比較常用到逕度版本的我貼逕度版本的
float fastAtanf_rad(float dy){
    static const float atan2_p1 =  0.9997878412794807f;
    static const float atan2_p3 = -0.3258083974640975f;
    static const float atan2_p5 =  0.1555786518463281f;
    static const float atan2_p7 = -0.04432655554792128f;
    static const float atan2_DBL_EPSILON = 2.2204460492503131e-016;

    float ax = 1.0, ay = std::abs(dy);
    float a, c, c2;
    if (ax >= ay) {
        c = ay/(ax + static_cast<float>(atan2_DBL_EPSILON));
        c2 = c*c;
        a = (((atan2_p7*c2 + atan2_p5)*c2 + atan2_p3)*c2 + atan2_p1)*c;
    } else {
        c = ax/(ay + static_cast<float>(atan2_DBL_EPSILON));
        c2 = c*c;
        a = M_PI*0.5 - (((atan2_p7*c2 + atan2_p5)*c2 + atan2_p3)*c2 + atan2_p1)*c;
    }

    if (dy < 0)
        a = - a;

    return a;
}
這一分我有做簡單的驗證是沒問題的,可以參照底下的代碼有過程,敏感場合建議自己在想方法驗證一次~

其他

全部一共4個版本,atan()和atan2() 以及他們的 逕度版本與角度版本。

2017年9月29日 星期五

電腦休眠睡眠之後莫名其妙或動到滑鼠自己喚醒

電腦休眠睡眠之後莫名其妙或動到滑鼠自己喚醒


很多時候之所以不關電腦僅僅只是因為這樣方便,開啟的程序在明天之後又可以直接開始,其實也不用這麼特別費電,除非是需要連網的程式,不然使用休眠或睡眠就可幫你保持當前狀態,並且關閉電腦。
睡眠跟休眠差異不大,差異在於睡眠不會將記憶體斷電,記憶體一旦斷電資料就全沒了,所以睡眠的喚醒速度會快很多;但是如果遇到停電,比如說把插頭拔掉,那麼記憶體的東西就全沒,會變成休眠狀態記憶體的副本在你進入睡眠的時候會移併複製一份;比較簡單的分辨方法就是電源燈與硬碟燈會慢速閃爍。
至於更古老以前的XP所有的待機那又是另一回事。
新版的Win10已經沒有休眠這個選項了,剛剛也有提到其實睡眠也包含休眠,不過如果是在自己的房間睡覺的時候一閃一閃的也是很受不了。
要開啟休眠可以到控制台的電源選項
然後可以從這裡開出來

自動喚醒

不過在用的人應該不少人有這樣的困擾,為什麼休眠中的電腦會突然自己喚醒,或是動到滑鼠就喚醒了(撞到桌子連動感應到滑鼠),印題出自於以下
  • 滑鼠喚醒
  • 網路喚醒
網卡一般預設應該是關掉的,但是如果你確定沒動到滑鼠還是自己開機或是筆電連上wifi就自己開機,八成是網卡惹的禍。
對著左下的win按右鍵,找到裝置管理員可以打開他,然後找到你的網卡打勾
只允許 magic 封裝喚醒電腦
不然你會因為一堆莫名其妙的原因導致網卡啟動電腦。

滑鼠

再來處理滑鼠,可以透過這個指令查看那些裝置具有喚醒權限
powercfg -DEVICEQUERY wake_armed
看不爽的都停掉也是個辦法,我自己是有留鍵盤方便開機。
值得注意的是,有些滑鼠會有兩個以上的裝置!比如說羅技有3個,有太陽的無線接收器可能是因為可以接收6個裝置,所以同時有了鍵盤與滑鼠2個,3個都要關掉,不然滑鼠動到還是會喚醒。
關掉的方法如下。
至於有幾個妳就把滑鼠拔掉看看少幾個就知道,然後可以從ID去看說到底是哪一個。
這樣就不會在莫名其妙自己喚醒了。

參考

2017年9月24日 星期日

Onedrive 已同步的綠色打勾消失不見

Onedrive 已同步的綠色打勾消失不見

我的電腦已經不見許久,不見的原因是因為系統還原過,最近新增了第二個Onedrive帳戶,沒想到新增完畢之後綠色就打勾就自己修復了!
從這裡新增第二個帳戶
新增完畢之後需要重新啟動電腦
可能會遇到的問題是系統變數 %onedrive% 會變成第二個帳戶,解決方法也很容易,自己到環境變數內修改即可。
更改回你要的位置
困擾了好久,意外解決了QuQ

2017年9月17日 星期日

Win10 1703 icon 圖示亂跑 插入圖示中間其他圖示跑掉

Win10 1703 icon 圖示亂跑 插入圖示中間其他圖示跑掉

如果直接全新安裝貌似不會有這個bug如果是升級上來會有這個問題,尤其是筆電

處理方法

有三個關鍵原因導致的
  • 升級1703非全新安裝
  • 貼齊格線
  • 文字放大

升級1703非全新安裝

只能重灌了,不能重灌就以下方法加減用~
如果是1604升級上來不會有事情,但是如果超過兩個版本也就是從1604之前一路升級上來的就會有這個狀況。

貼齊格線

右鍵→檢視→貼齊格線
直接不貼齊格線就正常了,不過可能要習慣一下。
或者也可以關掉之後重新打開也會恢復正常,但是重新開機之後又會不正常,又要重按一次。

文字放大

或者桌面→右鍵→顯示設定→縮放與版面配置改回100%也就正常了。
不過對於2K以上的螢幕或是筆電不得不放大,只能忍了或重灌。

2017年9月9日 星期六

Harris corner 角點偵測源代碼 source code

Harris corner 角點偵測源代碼 source code

bool harris(const vector<float>& p,
    size_t w, size_t y, size_t x)
{
    // 閥值
    constexpr float r = 10;
    constexpr float thre = ((r + 1)*(r + 1)) / r;
    // 二維讀取
    auto at2d = [&](int y, int x) {return p[y*w + x];};
    // 公式
    float Dxx = 2 * at2d(y, x) - at2d(y, x-1) - at2d(y, x+1);
    float Dyy = 2 * at2d(y, x) - at2d(y-1, x) - at2d(y+1, x);
    float Dxy = at2d(y+1, x+1) + at2d(y-1, x-1)
        - at2d(y-1, x+1) - at2d(y+1, x-1);
    Dxy /= 4;
    float Tr = Dxx + Dyy;
    float Det = Dxx * Dyy - Dxy*Dxy;
    // 判斷閥值
    if ((Tr*Tr / Det) < thre) {
        return 1;
    } return 0; 
}
沒有針對邊緣防呆,記得避開邊緣的點
測試圖

2017年9月7日 星期四

Visual Studio 17 更新後 win32主控台 消失不見

Visual Studio 17 更新後 win32主控台 消失不見

其實還在,只是換了一個名字,在這裡
然後下一個頁面也改了,不過內容還是一樣UI變得比較簡潔

僅支援 IE6 以上版本瀏覽器,Win10 無法打開

僅支援 IE6 以上版本瀏覽器,Win10 無法打開

有些網站太過老舊沒辦法直接打開

以下是打開方法

搜索 ie
打開之後先開到你要的網站,然後從這裡案相容性
這裡範例的是
葛瑪蘭汽車客運 http://secure.kamalan.com.tw/common_order.php
有先打開就會自動幫你填網站了直接新增就好
然後就可以用了。

2017年9月6日 星期三

新版 rar 如何直接解壓縮到目錄 不解壓縮到 C槽 TEMP

新版 rar 如何直接解壓縮到目錄 不解壓縮到 C槽 TEMP

爬文看網路上很多的說法是路徑改成 \ 的,但是實際操作還是跑到C曹去了測試了一下幾個路徑有不同的結果。

不使用暫存

不會跑到別的地方去的方法是
  • 直接右鍵,然後選擇 解壓縮到...解壓縮至此
大檔案儘可能使用這個方式解壓,不要用拖曳的拖式解壓出來。

使用暫存

如果是,打開進RAR然後再拖曳解壓縮,就會解到暫存目錄。
在WinRAR設定中的,暫存目錄根據以下的設定有不同的結果。
有無打勾僅使用存取式硬碟並不影響。(應該影響上面的解壓縮方式)
  1. 暫存目錄在壓縮檔旁邊
    .\
    
  2. 暫存目錄在使用者文件
    ..\
    
  3. 暫存目錄在C
    ...\
    
  4. 暫存目錄在temp
    ~\
    

2017年9月5日 星期二

C/C++ 編譯時如何更換圖示 gcc 與 Visual Studio

C/C++ 編譯時如何更換圖示 gcc 與 Visual Studio

Visual Studio

這個比較簡單只要直接加入資源即可
這裡要注意如果是ico選ico才看的到
然後依據你的編譯模式 x86/x64 和 debug/release 找到相應的檔案,或者你退到最外面直接在右上角搜索 *.exe 也可以,就可以看到圖示了。

gcc

這個比較複雜一點,寫了一個程序可以製作檔案,直接下載使用即可。

產生資源檔

先把你的圖示ico與下載的檔案create_resource_release放在一起,然後直接拉進去就可以產生了。
或者用命令
create_resource_release.exe CHG.ico
你可以一次拉多個檔案進去
即可產生 CHG_icon.o
在來編譯的時候只要加入他即可,例如我編譯一個cpp sou.cpp 下這樣的命令
g++ CHG_icon.o sou.cpp -o sou.exe
產生出來的檔案就有圖示了