2018年3月28日 星期三

Win10 關機黑畫面很久才關閉 或是 開機進桌面前會黑屏一段時間

Win10 關機黑畫面很久才關閉 或是 開機進桌面前會黑屏一段時間

懶人包

  1. 確定網卡已經安裝廠商提供驅動,而不是Win10自動安裝
  2. 確實安裝主機板晶片驅動程式,尤其是英特爾的IME一定要
  3. 到 控制台->電源->關閉快速啟動 (見下面第一張圖)
下面是相關知識說明,上面做完即可解決問題可以不用看
另外這個現象我在2018的Win10還沒沒發生過,有可能是bug被修好了,也有可能是升級Win10版本導致的,有季度大更新建議重灌比較穩~


快速啟動

關機黑畫面比較常發生,是快速啟動導致的。但是正常情況下的快速啟動是不至於卡到的,大概就是延遲個幾秒就關閉了,不曉得是原因導致卡住了,如果卡太久就把它關閉了吧。
快速啟動會再關機的時候把一些硬件資訊或是開機的東西寫入硬碟,下次開機直接讀來用就不用重新讀取了。
快速啟動條件比較嚴苛要
  1. 系統支援並打開
  2. 主機板支援並打開
  3. 顯示卡支援(會更快)

系統支援並打開

到電源選項開啟即可,關機卡太久也是從這裡關閉就好

主機板支援並打開

到主機板裡面打開 fastboot+Ultra Fast ,兩個可能在同一選項。
Ultra Fast 會關掉一堆咚咚的檢測,比如說鍵盤…,所以才會有人說 WIN10的筆電不能進BIOS之類的問題。(解決辦法就是按住SIFT再按重開機就會跳出安全模式選項,選項中有一項是進BIOS的)
我的華碩Z97板子並沒有Ultra Fast,不過他可以手動選擇那些東西不檢測,結果是差不多意思的,手動把全部關了都不檢測;Ultra Fast是主機板廠商幫你設置好,廠商直接特化的會更快一些。
另外硬盤必須是GPT的格式,使用EFI啟動才能Ultra Fast,如果你的硬盤是MBR可以參考站內的文章,使用微軟內建的工具轉換,一下子就好了。
https://charlottehong.blogspot.tw/2017/11/windows-mbr-gptefi.html

顯示卡支援

顯示卡支援才能打開 Ultra Fast ,具體可以查詢一下,不過現在已經2018了市面上的卡都支援了,除非是舊卡否則不用特別查都會有。

快速啟動失敗

有沒有成功可以由幾個地方觀察(下面要同時成立)
  1. 開機是主機板廠商LOGO+轉圈圈而不是win10
  2. 這個轉圈圈只會一次轉個幾圈直接進桌面
快速啟動硬件都支援也都打開了,也有可能會失敗,失敗的原因不清楚,不過這就是造成標題所導致的情況的主因。
這邊以我的主機板華碩,具體可以看到開機的時候會有幾秒顯示 del可以進BIOS,會看到這個畫面就是快速啟動失敗了…,正常應該是直接ASUS轉一圈進桌面。
還有另一種失敗情況,一開機 ASUS轉圈,然後閃一下 ASUS再一次轉圈。
失敗都是有徵兆的,上一次關機會關超久,卡在黑畫面

如何修復快速啟動

如果失敗的話嘗試看看
  • 關閉快速啟動,正常關機再開機,然後再打開快速啟動
  • 清理一下你的C曹的至少保留足夠的空間
都不行就直接關了吧,SSD最多不會差超過10秒
都弄不好可以試試在開始搜尋復原,然後選擇保留資料。這個就相當於重灌,不過會幫你留著資料,原本設置是什麼就還好什麼完整保留,只重灌系統部分。
之前有一次大改版年度更新之後,我的快速啟動就修好了XD 回推這個方法可以試試看。
不過為了避免意外發生記得還是要先備份資料。

2018年3月21日 星期三

C C++ 隨機亂數 如何每次執行程式都不一樣

C C++ 隨機亂數 如何每次執行程式都不一樣

一開始時做亂數的時候最常遇到的問題就是怎麼每一次執行的亂數都一樣,大概是幾個原因造成的,沒有給時間種子或者是把時間種子放到for裡面一起跑了。
下面是做好的範例版本,初始化的方法是用 static 避開,static 初始化的那一行只會執行一次,然後這個變數會一直留著不會被刪除(解構)直到程序結束才跟著解構。

範例代碼

預設函式都是不包含最大值的,使用的時候可以自己手動在乎叫的時候+1,或是從函式裡面把 --max 中的 -- 移除即可。
純C的版本
/*****************************************************************
Name : 
Date : 2018/03/21
By   : CharlotteHonG
Final: 2018/03/21
*****************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int rand_int(int min, int max){
    static int init=0;
    if(init == 0){ srand((unsigned)time(NULL)), rand(), ++init; }
    return (int)((rand() / (RAND_MAX+1.0)) * (--max - min + 1.0) + min);
}

int main(int argc, char const *argv[]){
    for(unsigned i = 0; i < 10; ++i) {
        printf("%d, ", rand_int(0, 10));
    } printf("\n");

    return 0;
}
再來是C++的版本
C++版本中
VS2017 使用的預設引擎是 mt19937
GCC 有查到使用的預設引擎是 minstd_rand0 (這個會導致第一個亂數永遠一樣)
/*****************************************************************
Name : 
Date : 2018/03/21
By   : CharlotteHonG
Final: 2018/03/21
*****************************************************************/
#include <iostream>
#include <random>
#include <ctime>
using namespace std;

template<typename T>
int randNum(int min, int max) {
    static std::mt19937 generator(time(NULL));
    std::uniform_int_distribution<T> distribution(min, --max);
    return distribution(generator);
}

int main(int argc, char const *argv[]){
    for(unsigned i = 0; i < 10; ++i) {
        cout << randNum<int>(1, 10) << ", ";
    } cout << endl;
    return 0;
}

為何亂數的第一個永遠都一樣

引用至:https://goo.gl/WJDCR7 留言中提到以下內容
我查到的是 default_random_engine 是使用 minstd_rand0引擎,使用的是linear_congruential_engine
算法大致為 xi+1 = (16807xi + 0) mod 2147483647.
那個如果是用time(NULL)
我們假設目前time是137486376xxxx(x我不知道是多少不過沒差)
要生成數字為0 - 168070000經過測試第一筆亂數為
136956956xxxx * 16807 / 16807000 = 136956956
這時time為
第二次跑程式時測是為生成 0-168070000
137490578xxxx * 16807 / 16807000
由於生成的數字太小,time的前幾位數並沒有改變所以導致
第一個數字都會一樣
而事實上,當你生成的亂數max等於100000時
第一次的第一個數 : 77018
//間隔約3秒
第二次的第一個數 : 77021
可以發現只有各位數在改變
而雖然microsoft 的定義是typedef mt19937 default_random_engine;但本質上用的引擎仍為mt19937,使用的是梅森算法(別問我是甚麼),所以不會有這問題
細節還是問其他懂的人吧
我以前用亂數時也發生過這問題所以我通常會用time(clock())

2018年3月19日 星期一

為什麼 char* str 在 printf 的時候 str 與 *str 會印出一樣的內容

為什麼 char* str 在 printf 的時候 str 與 *str 會印出一樣的內容

    char str[][1] = {{1}};
    printf("%p\n",  str);   // is address
    printf("%p\n",  *str);  // is address
    printf("%d\n", (int)    *(char*)str); // is 1

第一行

返回的就是一個陣列的地址這個應該不是個大問題,只是這中間編譯器自己默默做了一次隱式轉換
str 是一個 char(*)[1] 型態 自動轉換成 (char**)型態 他們之間並不完全等價,前者是陣列後者是指針。
這裡實際輸入函式的型態是 (cahr**)

第二行

這個是因為陣列轉換後的指針正好會是第一個值的關係,所以derefrence之後之後還是同一個地址
這裡實際輸入函式的型態是 (cahr*), 由 (char**) dereferance 而來。

第三行

這裡多一個語法上不安全的型別轉換,從 (char**) 轉換到 (char*)
(這個例子使用上是安全的)。
轉換成指針之後再 derefernnce 獲得記憶體內容,最後再轉成整數印出。
因為放置的內容 1 所以就是 1 了,如果內容是 1 對照一下表會是 49
    char str[][1] = {{1}};
    printf("%p\n",  str);   // is address
    printf("%p\n",  *str);  // is address
    printf("%d\n", (int)    *(char*)str); // is 1

2018年3月17日 星期六

C++ 計時器,計時函式跑的時間

C++ 計時器,計時函式跑的時間

簡單寫了一個比較易用的函式
/*****************************************************************
Name : Timer.hpp
Date : 2017/12/19
By   : CharlotteHonG
Final: 2017/12/19
*****************************************************************/
#pragma once
#include <string>
#include <ctime>

class Timer {
public:
    Timer(std::string name=""): name(name){
        startTime = clock();
    }
    operator double() {
        return time;
    }
public:
    void start() {
        startTime = clock();
        flag=0;
    }
    void end() {
        finalTime = clock();
        time = (double)(finalTime - startTime)/CLOCKS_PER_SEC;
    }
    void print(std::string name="") {
        if (flag==0) {
            flag = 1;
            end();
        }
        if(name=="") {
            name=this->name;
        }
        if(priSta) {
            std::cout << "#" << name << ", " << " time = " << time << "s" << std::endl;
        }
    }
private:
    std::string name;
    clock_t startTime;
    clock_t finalTime;
    double time;
    bool flag = 0;
public:
    bool priSta = 1;
};
直接引用即可,使用方法大致如下
Timer t;
t.start();
YourFun();
t.print("YourFun");
如果有一整頁的計時器一個一個刪除很麻煩可以使用
t.priSta=0;
可以關閉計時,就不用砍掉全部了

2018年3月16日 星期五

透視投影(WarpPerspective) 與 仿射投影(WarpAffine) 實作範例代碼(source code)

透視投影(WarpPerspective) 與 仿射投影(WarpAffine) 實作範例代碼(source code)

他們指的是一個影像投射到不同視覺角度上的變化情況

透視投影

  • 是3D的
  • 投影完維持四邊形,直線還是直線但不再平行
可以查看那些鐵軌的圖片,會發現鐵軌越往遠處就會越靠近而不是平行的,儘管他們實際上是平行的,鐵軌的遠方就是透視點。(透視點也是繪圖困難的地方,尤其是人體很吃過往的經驗)
公式
假想有一張圖
把灰色的點固定不動,紅色的點任意你移動,圖片會跟紅點動,可以假想這是一個氣球的皮上面畫有圖像然後你對紅點任意 [移動or縮放] 的的概念。

仿射投影

  • 是2D的
  • 投影完會維持平行四邊形,原本平行的東西還是平行
公式其實與透視投影一樣,取前6個就是第三排Z直接不要了
一般表示方法會 m31=m32=0 m33=1
這樣一來一樣套原本的算法就不需要再除上Z了,式子帶入會被消除。
延續剛剛的概念,現在把紅點刪除一個(或者是說兩個點永遠同步移動)
他們永遠都會是平行四邊形,只是可能出現各種形狀

參考代碼

下面是程式核心的轉換代碼
轉換有分兩種,同一個矩陣可以是
原圖座標轉換到新圖座標,也就是一般看到的公式
另一種反轉是透過計算 反矩陣(逆矩陣) 而來的,詳細可以參考
https://www.wikiwand.com/zh-tw/%E9%80%86%E7%9F%A9%E9%98%B5
只是把它反過來而已,程式中我已經幫你寫上了。
有一點要注意的是,轉換過去再轉換回來會有一些誤差。
// 輸入 dst 座標, 反轉 scr 輸出.
void WarpPerspective_CoorTranfer_Inve(const vector<double>& HomogMat, double& x, double& y) {
    const double* H = HomogMat.data();
    const double i=x, j=y;

    x = (H[2] - H[8]*i) * (H[4] - H[7]*j) - 
        (H[1] - H[7]*i) * (H[5] - H[8]*j);
    y = (H[0] - H[6]*i) * (H[5] - H[8]*j) - 
        (H[2] - H[8]*i) * (H[3] - H[6]*j);

    double z = (H[1] - H[7]*i) * (H[3] - H[6]*j) - 
        (H[0] - H[6]*i) * (H[4] - H[7]*j);

    x /= z;
    y /= z;
}
// 輸入 scr 座標, 轉換 dst 輸出.
void WarpPerspective_CoorTranfer(const vector<double>& HomogMat, double& x, double& y) {
    const double* H = HomogMat.data();
    const double i=x, j=y;

    x = H[0]*i + H[1]*y +H[2];
    y = H[3]*i + H[4]*y +H[5];
    double z = H[6]*i + H[7]*y +H[8];

    x /= z;
    y /= z;

    //x=round(x);
    //y=round(y);
}

參考

2018年3月14日 星期三

OpenCV Mat 讀取數值錯誤,如何讀出正確數值

OpenCV Mat 讀取數值錯誤,如何讀出正確數值

在做仿射轉換的時候發現讀出來的數值都怪怪的,找了很久,後來才想到應該是要用 double,下面是讀取的範例。
    //選定幾何轉換前後相對的三個點
    Point2f srcTri[3];
    srcTri[0] = Point2f(0, 0);
    srcTri[1] = Point2f(10, 0);
    srcTri[2] = Point2f(0, 10);

    Point2f dstTri[3];
    dstTri[0] = Point2f(10, 10);
    dstTri[1] = Point2f(20, 10);
    dstTri[2] = Point2f(10, 20);

    Mat warp_mat = getAffineTransform(srcTri, dstTri);

    cout << warp_mat << endl;
    cout << "=========================" << endl;

    for(size_t j = 0; j < warp_mat.rows; j++) {
        for(size_t i = 0; i < warp_mat.cols; i++) {
            cout << warp_mat.at<float>(j, i) << ", "; // 錯誤
            //cout << warp_mat.at<double>(j, i) << ", "; // 正確
        } cout << endl;
    } cout << endl;

Win7 boot Error Code: 0xc0000225 repair 修復方法

Win7 boot Error Code: 0xc0000225 repair 修復方法

delete all partition,
then rebuild partition and install windows.


可以見到 bcd 選單基本上開機檔都ok沒問題了~
不用去修復這些了,會造成這麼情況就算重灌也沒用
我確實也嘗試過修復,以及多次重新安裝,就是都一樣
後來找到的原因好像是因為分割區的問題,刪除所有磁區然後重新切割再安裝即可~
我這顆硬碟有點問題,有跳C6的黃燈,所以如果你有見到這個錯誤,很有可能是硬碟的問題導致的,分割表那邊的磁軌正好出問題,檢查一下比較保險~