2018年6月15日 星期五

高斯核心矩陣 如何計算 範例程式

高斯核心矩陣 如何計算 範例程式

取自 opencv 函式轉成一般 vector 函式
比較特別的是,他的計算方法如果半徑小於7的話,標準差sigma又是無效設置的話(小於1)就用內定的矩陣,而不再重新計算。
vector<double> getGaussianKernel( int n, double sigma )
{
    const int SMALL_GAUSSIAN_SIZE = 7;
    static const float small_gaussian_tab[][SMALL_GAUSSIAN_SIZE] =
    {
        {1.f},
        {0.25f, 0.5f, 0.25f},
        {0.0625f, 0.25f, 0.375f, 0.25f, 0.0625f},
        {0.03125f, 0.109375f, 0.21875f, 0.28125f, 0.21875f, 0.109375f, 0.03125f}
    };

    const float* fixed_kernel = n % 2 == 1 && n <= SMALL_GAUSSIAN_SIZE && sigma <= 0 ?
        small_gaussian_tab[n>>1] : 0;

    vector<double> kernel(n);
    double* cd = kernel.data();

    double sigmaX = sigma > 0 ? sigma : ((n-1)*0.5 - 1)*0.3 + 0.8;
    double scale2X = -0.5/(sigmaX*sigmaX);
    double sum = 0;

    int i;
    for( i = 0; i < n; i++ )
    {
        double x = i - (n-1)*0.5;
        double t = fixed_kernel ? (double)fixed_kernel[i] : std::exp(scale2X*x*x);
        cd[i] = t;
        sum += cd[i];
    }

    sum = 1./sum;

    for( i = 0; i < n; i++ )
    {
        cd[i] *= sum;
    }

    return kernel;
}
使用範例
// 核心
    vector<double> gau = ::getGaussianKernel(3, 0);
    for (size_t i = 0; i < gau.size(); i++) {
        cout << gau[i] << ", ";
    } cout << endl;

// 驗證
    vector<double> gau = ::getGaussianKernel(9, 1.6);
    cout << "[";
    for (size_t i = 0; i < gau.size(); i++) {
        cout << gau[i] << ", " << endl;
    } cout << "]"<< endl;

    Mat g = cv::getGaussianKernel(9, 1.6);
    cout << g << endl;

2018年6月14日 星期四

enable_shared_from_this 用途與使用範例

enable_shared_from_this 用途與使用範例

主要是用來處理 share_ptr 發生重複 delete 問題,下面用範例來說明問題
共享記憶體的本意,本意為
作為代理的物件可以任意複製,直到所有代理的物件被銷毀到最後一個才解構,代理所指向的物件的記憶體;即你可以複製或製作一堆共同指向地址p的指標,最後只解構一次p指向之物。
下面是正確的例子,有兩個指標 p 與 q 共同指向同一個地址,但只解構一次。
class Test {
    public:
    ~Test() {
        cout << this;
        cout << " Test Destructor." << endl;
    }
};

    shared_ptr<Test> p(new Test());
    shared_ptr<Test> q = p;
再來是出問題的例子,在嘗試讓函式返回自動指標的時候會出問題
class Test2: public enable_shared_from_this<Test2> {
    public:
    ~Test2() {
        cout << this;
        cout << " Test Destructor." << endl;
    }
    shared_ptr<Test2> getPoint() {
        return shared_ptr<Test2>(this);
};

// 錯誤1
    shared_ptr<Test2> p2(new Test2());
    shared_ptr<Test2> q2 = p2->getPoint();

// 錯誤2
    auto pp = new Test2();
    shared_ptr<Test2> p2(pp);
    shared_ptr<Test2> q2(pp);
這裡會對同一個地址執行兩次解構子,與共享記憶體的本意不符,至於為什麼會這樣可以從錯誤2來思考應該比較清晰,這裡兩個錯誤的原因是一樣的,只是錯誤1被包裝不容易看出,寫程序的時候可能會踩到坑。
不應該拿同一個指標來做兩個以上的自動指標,錯誤1的問題在於 return 的時候拿了 this 再做一次,這時候會沒有被記錄到程序會當作這是兩個不同的物件所以解構2次;正確的用法是一個指標只能產生一次自動指標,第二個以上的自動指標都要用自動指標產生/複製出自動指標,這樣物件就可以正確追蹤了。
錯誤2本來就不應該這樣寫直接拿 q2=p2 即可,錯誤1比較麻煩,環境所逼只能從成員函式拿指標的話那根本沒辦法避免(實際上為了保護,這種情況是很常見的手法)。
為此正確的處理方式就是使用 enable_shared_from_this<T> ,需要做兩個動作
  1. 繼承這個物件
  2. 返回 shared_from_this();
class Test2: public enable_shared_from_this<Test2> {
    public:
    ~Test2() {
        cout << this;
        cout << " Test Destructor." << endl;
    }
    shared_ptr<Test2> getPoint2() {
        return shared_from_this();
    }
};

    shared_ptr<Test2> p3(new Test2());
    shared_ptr<Test2> q3 = p3->getPoint2();
如此就能正常運作了。

參考

2018年6月12日 星期二

手機鋰電池過放電 充電充不起來

手機鋰電池過放電 充電充不起來

觸發條件

我猜可能是這樣做導致過放電的,我手機放沒電大概好幾天了然後晚上的時候我先是接上充電器然後等個1分鐘左右顯示SONY然後看到電池圖示1%,然後充到10%之後開機。
這是我的備用機太久沒開至少一周了,開機之後一堆程式在同步還蠻耗電的,然後我就沒管他想說不要放整晚10%就放著沒管了。
隔天之後按開機就再也按不起來了,然後充電也充不起來,完全就是死機紅色,我有用行動電源充電,結果過沒幾分鐘就斷電了,估計是都沒充進去低電流斷電了…

救援

然後我就接充電器放著1小時,還是什麼反應都沒有,怎麼按都不行,最後放棄了我把他拔掉了,想說再按按看在拔電狀態按開機,電源燈紅燈閃幾秒,就想說有救了!趕快接上然後就開起來顯示1%了。

原因

看了資料猜測開不起充不進去的原因應該是低於第二階段電壓,充電IC直接判定電池死了不給過電了。
有看到資料說一般是低於某一個電壓判定沒電保護過放,會把手機關機。如果再繼續低下去過放電,第二階段電壓這時候對電池充電有爆炸危險會判定死機直接不給過電。
斷電的時候按住開機,可能會導致IC嘗試開機暫時通電,所以就趕快差電接著充電進去就沒事了。
也有可能純粹是超低電壓的狀況下IC只給用超級小電流充電所以我充了1小時差不多剛好到可以啟動的電壓。不過我拔電之前我有嘗試開機真的不行才拔的。
大概是,快沒電的時候不要用重電,因為%數是看電壓決定的,假如現在3.6v 判定是1%,你在這時候吃重電有可能導致消耗太多功率(W),然後電池直接變成 3.4v 之類的而不是緩慢下降。

其他

我有買USB電流監控器,我發現充電時候打開螢幕使用會從 1.5A 變成 0.86A,再用重電一點會變成 0.5A,就是說邊充邊用為了保護電池是會降低充電的。
所以快沒電的時候不要邊充邊用,可能會扣到0%關機可能會像我這樣進入鎖定狀態,不小心扣到0%的時候建議至少充到10%才開機,不要再低電量使用,不然也是有可能用一用直接關機。

結論

  1. 手機待機放到沒電不會怎樣,記得別拖太久趕快充就好 (傷電池但是不會死給你看)
  2. 打GAME或是吃重電下,用到0%可能會導致放電過度 (可能會死給你看)

2018年6月11日 星期一

nvidia Tx2 火力全開解除限制 讓風散轉起來

nvidia Tx2 火力全開解除限制 讓風散轉起來

預設一般效能是受限的,要自己打指令打開受限的能力
先打開風扇(沒要大量運算可以不用開~待機或是只跑一下不會太熱)
sudo  ~/jetson_clocks.sh
切換模式
sudo nvpmodel -q verbose
sudo nvpmodel -m 0
在看一次模式
sudo nvpmodel -q verbose
顯示
NV Power Mode: MAXN
0
這樣就火力全開了

2018年6月8日 星期五

ubuntu 純指令安裝 chrome

ubuntu 純指令安裝 chrome

google chorme
wget -c https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo dpkg -i google-chrome-stable_current_amd64.deb
sudo apt-get install -f
VS code
wget -c https://vscode.cdn.azure.cn/stable/6a6e02cef0f2122ee1469765b704faf5d0e0d859/code_1.24.0-1528306776_amd64.deb
sudo dpkg -i code_1.24.0-1528306776_amd64.deb
sudo apt-get install -f

ssh 連接到 Win10 的 bash on ubuntu

ssh 連接到 Win10 的 bash on ubuntu

步驟

  1. 原本內建的 SSH 要移除重裝
  2. 因為 powershell 本身也可以被ssh連入會占用這個port,改變ubunut的port即可
sudo apt-get remove --purge openssh-server
sudo apt-get install openssh-server
sudo vi /etc/ssh/sshd_config # Change Port from 22 to 2222
sudo service ssh --full-restart
(最後一行每次重新開機都要重打一次,需要的話把她寫進 .bashrc)
然後試試看用 powershell 連連看
ssh chg@127.0.0.1 -p2222


字型問題

如果使用 xwindwos 來開程式需要裝中文字型
sudo apt-get install ttf-wqy-zenhei
還有一個地方要改
sudo vi /etc/fonts/conf.d/49-sansserif.conf
倒數第四行的 sans-serif 改成 ubuntu
<string>ubuntu</string>
安裝字型
sudo cp Hack-Regular.ttf /usr/share/fonts/truetype/
sudo fc-cache -f -v

參考

2018年6月6日 星期三

C / C++ HDR rgbe 讀檔 與 色調映射(toneMapping) 實作範例代碼

C / C++ HDR rgbe 讀檔 與 色調映射(toneMapping) 實作範例代碼

以前做的作業整理起來順便發表上來,使用的方法是最簡單的能看能主的方法,並沒有任何優化。

參考論文:

細節與對比強化之高動態範圍影像顯示方法
High Dynamic Range Image Display with Detail and Contrast Enhancement
本篇不是實現上述論文,實現的是他提到的先前的方法
基於這個公式做色調映射:
色調映射有分全域與局域(本文實作的是全域),全域就是同一個公式對整張圖的每個點做,比較簡單迅速,缺點是可能會有某些地方表現不好,通常是最亮或是最暗的地方。需要比較好看的話要做局域色調映射,針對特別亮或暗的地方做不同處理。

使用函式庫:

流程

  1. 讀取 rgbe 轉換為 rgb
  2. 轉換彩色模型至 Yxy
  3. 對亮度通道做色調映射(tone mapping)
  4. 色彩模型轉回 rgb
  5. 進行 gama 校正 (微軟 Win10 設置為 2.2)
  6. 完成 global tone mapping


讀取 rgbe 轉換為 rgb

使用函式庫 rgbe 讀取,如果要重刻過程其實還蠻麻煩的~
使用的資料結構
struct basic_rgbeData {
    int width;
    int height;
    rgbe_header_info info;
    vector<float> img;
};
讀取的程式
void rgbeData_read(basic_rgbeData& hdr, string name) {
    FILE* hdrFile;
    fopen_s(&hdrFile, name.c_str(), "rb");

    // 讀檔頭
    RGBE_ReadHeader(hdrFile, &hdr.width, &hdr.height, &hdr.info);
    // 要求空間
    hdr.img.resize(hdr.width*hdr.height*3);
    // 讀rgbe
    RGBE_ReadPixels_RLE(hdrFile, hdr.img.data(), hdr.width, hdr.height);

    fclose(hdrFile);
}
讀進來之後就可以獲得圖像的長寬與 rgb 值了,這裡的rgb值會是小數點介於 0~1 之間,可以直接全部*255,然後把圖片顯示出來就可以看到黑黑的圖像了。
大於255的就變成255,小於0的就變成0

轉換彩色模型至 Yxy

把色彩模型從 rgb 轉換至 Yxy,實際上轉了兩次 rgbe -> yxz -> Yxy,因為公式可以合併並不衝突,我把它寫在一起了。(Yxy只是將ZXY轉換到坐標系上)
這個做是為了分離出光源與彩度,需要做處理的是光源,彩度則維持原本的樣子。
void rgb2Yxy(const float* src, float* dst, int size) {
//#pragma omp parallel for
    for(int i = 0; i < size; ++i) {
        float a, b, c;
        a = (0.412453) * src[i*3 + 0]+
            (0.357580) * src[i*3 + 1]+
            (0.180423) * src[i*3 + 2];

        b = (0.212671) * src[i*3 + 0]+
            (0.715160) * src[i*3 + 1]+
            (0.072169) * src[i*3 + 2];

        c = (0.019334) * src[i*3 + 0]+
            (0.119193) * src[i*3 + 1]+
            (0.950227) * src[i*3 + 2];
        dst[i*3 + 0] = b;
        dst[i*3 + 1] = a / (a+b+c);
        dst[i*3 + 2] = b / (a+b+c);
    }
}
#pragma omp parallel for 是平行算的標記,下面接著的for迴圈會被展開來平行運算。

對亮度通道做色調映射(tone mapping)

這裡就是全文的重點了,來自於上面介紹的公式。
其中的 dmax 與 b 來源是依據論文所提的建議設置
size是圖像的指標dst的總長度
#define HDR_dmax 100.0
#define HDR_b 0.85

void globalToneMapping(float* dst, int size, float dmax, float b)
{
    constexpr int dim = 3; // 幾個通道
    constexpr int rgb = 0; // 選擇哪個通道

    float maxLum = dst[rgb];
    for(unsigned i = 1; i < size; ++i) {
        if(dst[i*dim+rgb] > maxLum)
            maxLum = dst[i*dim+rgb];
    }

    float logSum = 0.0;
    for(int i = 0; i < size; ++i) 
        logSum += log(dst[i*dim+rgb]);

    const float logAvgLum = logSum/size;
    const float avgLum = exp(logAvgLum);
    const float maxLumW = (maxLum / avgLum);
    const float coeff = (dmax*float(0.01)) / log10(maxLumW+1.0);

#pragma omp parallel for
    for(int i = 0; i < size; ++i) {
        auto& p = dst[i*dim+rgb];
        p /= avgLum;
        p = log(p+1.0) / log(2.0 + pow((p/maxLumW),(log(b)/log(0.5)))*8.0);
        p *= coeff;
    }
}

色彩模型轉回 rgb

跟剛剛的公式一樣只是反轉回來RGB模型
void Yxy2rgb(const float* src, float* dst, int size){
//#pragma omp parallel for
    for(int i = 0; i < size; ++i) {
        float a, b, c, newW;
        newW = src[i*3 + 0] / src[i*3 + 2];
        a = src[i*3 + 0];
        b = newW * src[i*3 + 1];
        c = newW - b - a;

        dst[i*3 + 0]  =  float(3.240479)*b;
        dst[i*3 + 0] += -float(1.537150)*a;
        dst[i*3 + 0] += -float(0.498535)*c;

        dst[i*3 + 1]  = -float(0.969256)*b;
        dst[i*3 + 1] +=  float(1.875992)*a;
        dst[i*3 + 1] +=  float(0.041556)*c;

        dst[i*3 + 2]  =  float(0.055648)*b;
        dst[i*3 + 2] += -float(0.204043)*a;
        dst[i*3 + 2] +=  float(1.057311)*c;
    }
}
轉回來之後就已經映射完畢可以輸出來看看,與一開始相比過亮與過暗的地方已經修正完畢可以看到細節,但是整體偏暗許多需要再進行下一步修正。

進行 gama 校正

最後要將亮度調整到作業系統適合的參數,圖像一般都在 1.0 ,而Windwos系統使用的是 2.2 要將它修正到正確的參數即可。
gama參數這個資訊可以從結構裡面我們一直沒用到參數 rgbe_header_info info; 裡獲得。
#define HDR_gama 2.2

void gama_fix(float* dst, int size, float gam) {
    const float fgamma = (0.45/gam)*2.0;
    float slope = 4.5;
    float start = 0.018;
    // 判定係數
    if(gam >= float(2.1)) {
        start /= ((gam - 2) * float(7.5));
        slope *= ((gam - 2) * float(7.5));
    } else if(gam <= float(1.9)) {
        start *= ((2 - gam) * float(7.5));
        slope /= ((2 - gam) * float(7.5));
    }
    // 校正像素
#pragma omp parallel for
    for (int i = 0; i < size*3; i++) {
        if(dst[i] <= start) {
            dst[i] = dst[i]*slope;
        } else {
            dst[i] = float(1.099)*pow(dst[i], fgamma) - float(0.099);
        }
    }
}

完成 global tone mapping

再來結構的裡的圖像就可以使用摟,把它印出來。
轉出來的數值還是 0~1 要把他們 *255
然後大於255的就變成255,小於0的就變成0
hdr.bmp("resultIMG/HDR_IMG.bmp");


專案原始碼

未封裝測試代碼可以參考 OpenHRD.cpp 中的 testMapping() 如下,比較容易讀懂在寫什麼。
rgbeData_writeBMP 是將 float* 輸出成 *.bmp 圖檔到硬碟上使用函式庫 OpenBMP
void testMapping(string name) {
    // read file
    basic_rgbeData hdr;
    rgbeData_read(hdr, name);
    rgbeData_info(hdr);
    //rgbeData_writeBMP(hdr, "resultIMG\HDR_non.bmp");

    // Mpping
    int imgSize = rgbeData_size(hdr);
    vector<float> Yxy(imgSize*3);

    rgb2Yxy(hdr.img.data(), Yxy.data(), imgSize);
    globalToneMapping(Yxy.data(), imgSize);
    Yxy2rgb(Yxy.data(), hdr.img.data(), imgSize);
    //rgbeData_writeBMP(hdr, "resultIMG\HDR_mapping.bmp");

    cout << hdr.info.gamma << endl;
    gama_fix(hdr.img.data(), imgSize, 2.2);
    rgbeData_writeBMP(hdr, "resultIMG/HDR_IMG.bmp");
}

這裡是完整的專案已經封裝成完整的物件:OpenHDR for VS2017