2021年6月12日 星期六

Line 跨系統聊天紀錄轉移

今年四月的時候,因手上安卓手機已經用了快五年,感覺已經不能再撐下去了,因此萌生換手機的念頭,而自己對手機的要求也不高,用起來不要卡卡就可以,然後身邊朋友總說蘋果手機用久都不太卡頓,所以這次我決定要來跳槽,剛好親戚有不要的二手iPhone 7 plus 128G,因而我就大膽的買下去!!

當然,拿到新手機很開心,但我馬上遇到一個問題,就是遇到跨系統時,Line的聊天紀錄似乎沒這麼好處理,所以我詢問一些有跳槽經驗的朋友,同時也上網查一些資料,得到的結果大概就兩種,第一種是官方說法,正常無法轉移聊天紀錄,所以蠻多人直接放棄轉移,若有聊天紀錄需要,則會從電腦版的Line去查找;第二種就是訪間有些付費軟體可以協助轉移,但過程也小複雜....後來我想想,其實也沒有非得保留舊聊天紀錄,所以決定放棄轉移了,直接另起爐灶。

但,當我將Line從安卓轉到蘋果時,我依照正常流程輸入手機號碼,且事前沒有備份到iCloud雲蝶(廢話),所以我選擇不復原聊天紀錄,常理來說進入到主畫面時,應該要是全空的狀態,但.....神奇的事情發生了,我點開我的蘋果 Line,竟發現有聊 ! 天 ! 紀 ! 錄 !    突然覺得大腦充滿困惑,我到目前也完全不知道怎做到的,可能是老天賞賜給我的奇蹟吧!

事後想想,唯一的可能性,起先因為app剩Line還沒完全轉移,所以有一段時間我是安桌與蘋果手機同時使用,因為比較常使用Line,所以我幾乎都用安卓,然後分享網路給蘋果,聽起有點搞笑,哈哈哈,但這可能是個關鍵!!猜想會不會因為我在安卓分享網路給蘋果狀態下,再進行Line的轉移動作,間接也將聊天內容轉到蘋果呢?但過了一段時間後,原先以為全都轉移的聊天紀錄,其實只將二月到四月這範圍的聊天紀錄轉移過來,去年甚至更久以前的就都沒有轉過來,但至少有總比沒有好!!

最後,這個方法我並沒有再回去測試一遍,因為怕到時就真的全空了!但對於某些人如果跟我一樣,原先想放棄舊聊天紀錄,若這個方式可以幫你/妳保留近兩個月的聊天內容,其實我覺得還不錯,不會出現空白聊天視窗,然後不知怎麼接的感覺,整體來說我覺得是可以嘗試看看,萬一沒有成功,其實心裡也不會太難過,但如果成功了,我覺得心裡應該會蠻爽的!!!!!

僅供參考瞜~

2021年5月24日 星期一

心情札記-2021.05.24

今天窩了一整天的宿舍,內心被程式與高溫弄得有點心煩,趁著傍晚天氣較涼到附近公園溜搭,走著走著,竟傳來久違的夏天氣息,那就是在樹上高歌的蟬聲,如此熱鬧般的蟬聲,對比人行道空冷的寂靜,顯得格外諷刺,看來這個夏天依舊不寧靜,願這疫情能早點結束,讓生活回到從前般的開心!!

2021年3月30日 星期二

Git 版本管控(一)-淺談版本管控

最近,部門說要導入這個,然後由我做教學?!?!,既然都這樣安排了,就來整理自己目前對Git的了解,預計分享的內容如下:
  1. 淺談版本管控
  2. Git的環境建置
  3. Git本機操作
  4. Git遠端操作
淺談版本管控(Version Control)
首先,為什麼要使用版本管控(Version Control)呢?說說自己以前在研究所時期,剛開始寫C/C++跟OpenCV時,往往就是修改了一下程式或寫一些功能之後,為了避免後續程式碼可能被改到不能用,就會加上時間做區隔,類似做備份使用,但久而久之備份的內容越來越多,有時想還原回正常版本卻忘記不知哪邊被改動,因為距離上次修改太久了,所以只能細部查找差異性,間接花了很多時間在做非功能性開發,這個狀況持續到我工作接觸到Git才有所改變,此時發現原來寫軟體除了邏輯要夠用之外,其實蠻多小工具也要學會,因為正式的專案其架構都蠻大的,所以工作上會切分成寫人機介面(前端)演算法(後端)而某方寫完功能後一定會做測試,後續並做整合測試等等,因此若用改時間或註解紀錄程式碼修改差異,其效率或除錯速度都不好,所以才會出現版本管控(Version Control)這個概念。

更進一步的說,寫程式除了功能寫完之外,一般還會有整合、測試、進版等等,所以不是單單寫完程式就沒事!!畢竟如果寫出一堆Bug給別人就尷尬了,或是自己未來要除錯也不容易,總結來說版本管控(Version Control)就是用來記錄程式碼每次更改的差異,並能協助我們紀錄特定版本,若出現某版本因修改後而不穩定時,還能還原先前穩定版本,同時能找到是誰偷放這個臭蟲,哈哈哈哈,然後發現原來是自己搞的鬼!

版本管控系統(Version Control Systems)
    版本管控常用的工具,可分為集中式(Centralized Version Control Systems)與分散式(Distributed Version Control Systems),集中式顧名思義就是說將其程式碼存放在遠端的伺服器(Server),因此每次的提交(Commit)都必須要連到這個伺服器(Server)才行,本機端是無法做提交(Commit)動作,同時企業使用時可能要付費,若伺服器(Server)不小心發生什麼毀損,程式碼可能就會不翼而飛,其架構如下:



    相反的分散式則可以在本機端進行提交(Commit),同時也能夠上傳(push)到伺服器(Server),以我來看其實分散式的伺服器(Server)也可以視為是一種本機,只是相對於我們它是遠端而已,重點是它是免費的,免錢的最好啦!!其架構如下所示


後續會以分散式管控的Git做教學,預計會說明如何搭配Gui+Git做使用~~

2021年3月20日 星期六

C# 使用C++ DLL檔(回傳值)

延續上篇(C# 使用C++ DLL檔)之後,一般使用DLL時,除了將參數提供給DLL演算法做使用,也需將演算法結果從DLL回傳回去,所以這次就來說明如何回傳結果給C#吧。

目前我了解到如果要從DLL回傳結果給C#,都會使用指標做回傳,而我先前測試經驗,C++撰寫上比較不會遇到問題,但在接收端C#有遇過接不到數值、接錯值、或是跑久還造成程式當掉,重點是DLL除錯還不怎麼容易,像是瞎子摸象的感覺,如果有人知道怎樣較快除DLL的Bug,再歡迎留言告訴我,感謝!!!經過一番的測試之後,我目前可正常回傳的資訊分別為字串與float陣列資訊。

Extern.cpp

#ifdef SYSALGORITHM_EXPORTS
#define SYSALGORITHM_API __declspec(dllexport)
#include "SysAlgorithm.h"

extern "C" SYSALGORITHM_API int add(int a, int b)
{
	Calculator Calculator;
	return Calculator.add(a, b);
}

extern "C" SYSALGORITHM_API int subtract(int a, int b)
{
	Calculator Calculator;
	return Calculator.subtract(a, b);
}

extern "C" SYSALGORITHM_API void getDLLValue(float * DLLvalue)
{
	Calculator Calculator;
	return Calculator.getDLLValue(DLLvalue);
}

extern "C" SYSALGORITHM_API void getDLLInfo(char * DLLInfo)
{
	Calculator Calculator;
	return Calculator.getDLLInfo(DLLInfo);
}

#endif

SysAlgorithm.h

#pragma once
#ifndef SYSALGORITHM_H
#define SYSALGORITHM_H

#include <fstream>
#include <string>

class Calculator
{
public:
	Calculator();
	~Calculator();

	int add(int a, int b);
	int subtract(int a, int b);
	void getDLLValue(float * DLLvalue);
	void getDLLInfo(char * DLLInfo);
};

#endif // !SYSALGORITHM_H 

SysAlgorithm.cpp

#include "SysAlgorithm.h"

Calculator::Calculator()
{

}

Calculator::~Calculator()
{

}

int Calculator::add(int a, int b)
{
	return(a + b);
}

int Calculator::subtract(int a, int b)
{
	return(a - b);
}

void Calculator::getDLLValue(float * DLLvalue)
{
	DLLvalue[0] = 0;
	DLLvalue[1] = 1.2;
	DLLvalue[2] = -1.2;
}

void Calculator::getDLLInfo(char * DLLInfo)
{
	char Info[20] = "return DLLInfo";
	strcpy_s(DLLInfo, strlen(Info) + 1, Info);
} 

C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace SysUI
{
    public partial class Form1 : Form
    {
        [DllImport("SysAlgorithm.dll", EntryPoint = "add", CallingConvention = CallingConvention.Cdecl)]
        private static extern int add(int a, int b);

        [DllImport("SysAlgorithm.dll", EntryPoint = "subtract", CallingConvention = CallingConvention.Cdecl)]
        private static extern int subtract(int a, int b);

        [DllImport("SysAlgorithm.dll", EntryPoint = "getDLLValue", CallingConvention = CallingConvention.Cdecl)]
        private static extern void getDLLValue([MarshalAs(UnmanagedType.LPArray)]float[] value);

        [DllImport("SysAlgorithm.dll", EntryPoint = "getDLLInfo", CallingConvention = CallingConvention.Cdecl)]
        private static extern void getDLLInfo(StringBuilder DLLInfo);

        private StringBuilder DLLInfo = new StringBuilder(20);
        private float[] DLLvalue = new float[3];

        public Form1()
        {
            InitializeComponent();
            label1.Text = add(3, 5).ToString();
            label2.Text = subtract(3, 5).ToString();
            getDLLValue(DLLvalue);
            getDLLInfo(DLLInfo);
            label6.Text = DLLvalue[0].ToString() + "," + DLLvalue[1].ToString("F2") + "," + DLLvalue[2].ToString("F2");
            label8.Text = DLLInfo.ToString();
        }
    }
}
 
為了可以正確取得從DLL演算法回傳參數,C#的DllImport這邊很重要!!記得要注意


結果圖:


2021年2月20日 星期六

OpenCV Trackbar

一般寫影像處理時,不同的環境條件所需設定的參數都不一樣,為了加快自己調整參數的速度,決定使用opencv 的tracker bar做即時調整,而opencv的 tracker bar是callback函式,所以當拉把被移動位置時,會主動跳到callback函式做動作,同時也將當前bar的數值給函式使用,如此一來,就可以即時顯示不同結果,實際使用非常方便,不然從程式端去調整很麻煩,如果大家有更好的做法,也歡迎分享一下~~

Trackbar主要有五個參數要設定,分別為Trackbar的標籤名稱、在哪個視窗顯示Trackbar、Trackbar最大值(最小值預設固定為0)、Trackbar當前數值、callback函式(引數內容固定)

若想要使用兩個Trackbar,只要在第一個參數(標籤名稱)提供另一個名稱就可以,如下所示
	createTrackbar("Param1", "Window", &Param1, SliderMax, on_trackbar);
	createTrackbar("Param2", "Window", &Param2, SliderMax, on_trackbar);


程式碼如下:
#include <cstdio>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

const int SliderMax = 100;
int Param1 = 50;
int Param2 = 0;

Mat GrayImg, OutputImg;

static void on_trackbar(int, void*)
{
	double Alpha = (float)Param1 / 50;
	double Beta = Param2;
	GrayImg.convertTo(OutputImg, -1, Alpha, Beta);
	imshow("Window", OutputImg);
}

int main(){

	Mat SrcImg = imread("dog.jpg", CV_LOAD_IMAGE_COLOR);
	namedWindow("Window", WINDOW_AUTOSIZE);
	
	cvtColor(SrcImg, GrayImg, CV_BGR2GRAY);

	createTrackbar("Param1", "Window", &Param1, SliderMax, on_trackbar);
	createTrackbar("Param2", "Window", &Param2, SliderMax, on_trackbar);

	waitKey(0);
	return 0;
}
結果圖:


或是



備註(參考資料):
1.https://docs.opencv.org/3.4/da/d6a/tutorial_trackbar.html





2020年12月31日 星期四

OpenCV findhomography(單應性矩陣)與warpperspective(透視變換)

隨著科技進步與市場競爭,許多公司會試著將機械手臂導入到產線上,同時搭配工業相機做位置辨識,如手眼機器人(eye to hand和eye in hand),其原因就是要配合現在少量多變化的產能需求,達到彈性製造。

為了做到手眼機器人的功能,因有兩個不同座標系(影像座標跟手臂座標),如下圖所示,因無法直接將影像座標給機器手臂使用,故需使用OpenCV findhomography(單應性矩陣),找出兩者之間的關係,再利用warpperspective(透視變換)做座標轉換。



又或物體因離相機遠近不同,雖然明明是一樣的距離,但從影像上看卻不一樣長,如下圖的伯朗大道一樣,近的道路很寬,遠的又小到不行。



而Homography其實就是一個3 x 3矩陣,如下圖所示,其中h_9設為常數1,同時可以發現Homography  矩陣尚有八個未知數,而一對影像與機械座標只能求出其中兩個解,表示在實際校正個過程,至少要有四對非共線影像與機械座標才能求出Homography 矩陣。


下列程式碼主要展示從一張有歪斜的影像中,經由座標轉換之後,擷取到一張沒歪斜影像,如下圖所示,左邊為歪斜影像,右邊則是方正影像,不過校正用的四組座標,我並非用影像處理計算而是簡單人員標記,所以可能看起來沒有到完全很正。


程式碼如下:
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main(){

	Mat SrcImg1 = imread("SrcImg.jpg", CV_LOAD_IMAGE_COLOR);
	Mat DstImg = Mat::zeros(Size(3500, 1500), CV_8SC1);

	vector PtData1, PtData2;

	PtData1.push_back(Point2f(513,627));
	PtData1.push_back(Point2f(233, 2000));
	PtData1.push_back(Point2f(3877, 2047));
	PtData1.push_back(Point2f(3855, 835));

	PtData2.push_back(Point2f(0, 0));
	PtData2.push_back(Point2f(0, 1500));
	PtData2.push_back(Point2f(3500, 1500));
	PtData2.push_back(Point2f(3500, 0));

	Mat HomogMat = findHomography(PtData1, PtData2, CV_RANSAC);
	warpPerspective(SrcImg1, DstImg, HomogMat, DstImg.size());
	imwrite("DstImg.bmp", DstImg);
return 0; }

SrcImg1

DstImg







2020年11月28日 星期六

Python with Anaconda and Visual Studio Code

因應這幾年來深度學習熱潮,來介紹一下如何使用Anaconda建立虛擬環境,並搭配使用Visual Studio Code做程式編輯,步驟如下:

步驟1:下載Anaconda,連結為https://www.anaconda.com/products/individual,下載64位元版本


然後下載VS Code,連結為https://code.visualstudio.com/,我自己習慣是下載免安裝版本比較好使用

步驟2:安裝Anaconda,這步驟其實很單純,基本上就是一直下一頁就是了









步驟3:啟動VS Conde後,先到擴充工具安裝Python,這樣後續才能挑選自己想要的直譯器(環境),如果想要中文介面也可一併下載中文包,如下圖所示。


步驟4:在Anaconda中建立虛擬環境,可根據不同專案需求加入不同package,以避免會互相干擾,例如我想建立一個專門開發OpenCV的虛擬環境,該如何做呢?

步驟4-1:開啟Anaconda Prompt.exe,一般可從windows 開始中找尋Anaconda 資料夾,如下圖所示。


步驟4-2(建立環境):輸入conda create -n Opencv python=3.6,代表建立一個Opencv虛擬環境,並安裝Python 3.6版本 




完成後,系統還會跟你說接下來可以做啥

步驟4-3(啟用環境):輸入activate Opencv,可發現原本的base變成Opencv,代表我們已經切換環境了


步驟4-4(安裝套件):輸入conda install --y -c menpo opencv,然後等待安裝完成,此外,還需再安裝opencv-python才能正確使用OpenCV lib,所以再輸入pip install opencv-python,然後慢慢等



步驟4-5(測試):進入Python 直譯器,並直接將下列程式碼複製貼上測看看(記得貼上時每一行前面不能有空白,不然會出錯),會直接跑出一個視窗,裡面為一張500 x 500黑色影像,等跑到cv.destroyAllWindows()時再按下Enter就結束了,這樣就能確認OpenCV lib是否有安裝成功。

import numpy as np
import cv2 as cv
img = np.zeros((500, 500, 3), np.uint8)
cv.imshow('Image test', img)
cv.waitKey(1000)
cv.destroyAllWindows()

步驟4-6(停用環境):輸入conda deactivate 或deactivate,但有些版本下deactivate會出現警告


步驟4-7(刪除環境):假設有個test環境,輸入conda env remove --name test,就會刪除其環境,如下圖所示


步驟1~4主要介紹如何安裝Anaconda與Visual Studio Code,並建立虛擬環境,而下個步驟則要說明如何用Visual Studio Code做程式碼的修改與測試。

步驟5:開啟Visual Studio Code,選擇一個練習用的資料夾,如TestFolder

新增一個檔案,test.py,然後將剛剛步驟4-5的程式碼貼到此py檔中


再來就是將
直譯器設為剛剛建立的虛擬環境OpenCV,如下圖所示。



步驟6:最後,右鍵點選test.py並選擇在終端機中執行Pytone檔案,如果有正常顯示結果,就代表這次環境安裝成功可使用摟,並且未來可直接在Visual Studio Code修改程式碼。


結果:

#opencv #Python #Anaconda #Visual Studio Code 


2020年11月22日 星期日

OpenCV Image Watch

最近,在閱讀OpenCV文件時,突然發現一個很酷的東西,就是Image watch,這個工具是Microsoft提供給開發者,在除錯的時候能快速的知道每個影像(矩陣)實際長怎樣,換作是我平常的習慣,我都會將想看的影像利用imread存下來後,再利用小畫家或是GIMP去看影像閥值怎樣定最好,但有時候要開來開去也是很麻煩,而Image watch,就能夠在除錯中直接提供影像基本資訊,像是影像長寬、深度、畫面亮暗等。

如果要使用Image watch,可以直接從網路上搜尋image watch vs2013(選擇你對應的版本),如連結(https://marketplace.visualstudio.com/items?itemName=VisualCPPTeam.ImageWatch)並直接下載,會得到一個ImageWatch.vsix檔案,直接安裝即可。

如果想要確認是否有安裝成功,可以開啟你的Visual studio後,點選工具->擴充功能和更新選項後,如果有看到Image watch已被安裝,就代表完成瞜。







再來就是說明如何使用,先調整為debug模式,並在想查找位置下設中斷點,當執行到中斷點的時候,點選檢視->其他視窗->Image watch,就會調出一個很酷的視窗,如下所示。


能夠藉由這個工具讓你知道目前記憶體已經載入的資訊,此外,滾動滑鼠滾輪能放大影像,並直接看到每個像素亮度大小,如下圖所示。


不過我目前最常只用來看亮度分布而已,其他功能都不太吸引我,哈哈,如果有新的使用心得再分享出來。


備註(參考資料):
1.http://docs.opencv.org/2.4/doc/tutorials/introduction/windows_visual_studio_image_watch/windows_visual_studio_image_watch.html#windows-visual-studio-image-watch





2020年10月25日 星期日

C++ 計算程式的執行時間

平常開發程式的時候,最常被討論的部分就是performance好不好,而一般判定方式就是去計算程式的執行時間。

為了計算程式執行時間,會使用一個函式其名稱是clock,但使用前要先include time.h才能用,而clock回傳的單位是毫秒(資料型態是double),若希望單位變成秒,則要再除CLOCKS_PER_SEC才行,此外,為了計算準確的執行時間,會使用Ctrl+F5以最佳效能下做測試。

測試OpenCV 色域轉換所需時間,程式碼如下:
#include <opencv2/opencv.hpp>
#include "time.h"
using namespace cv;
using namespace std;
double StartTime, EndTime;

int main(){

	//讀取影像
	Mat SrcImg = imread("dog.jpg", CV_LOAD_IMAGE_COLOR);
	Mat OutImg;

	//紀錄起始時間點
	StartTime = clock();
	cvtColor(SrcImg, OutImg, CV_BGR2GRAY);
	
	//紀錄結束時間點
	EndTime = clock();

	imshow("OutImg", OutImg);
	waitKey(1000);

	//因為clock回傳的都是ms,所以若要用s顯示則需要除CLOCKS_PER_SEC,CLOCKS_PER_SEC代表1000
	//測試執行時間,通常都是以Ctrl+F5,則能得到最準確的時間
	cout << " Time:" << (EndTime - StartTime) / CLOCKS_PER_SEC << "ms" << endl;
	return 0;
}

2020年9月27日 星期日

OpenCV Drawing Functions

 這次來介紹OpenCV常用的六個畫圖函式,分別有畫線(line)、畫圓(circle)、畫矩形(rectangle)、畫橢圓(ellipse)、畫多邊形(polylines)、文字顯示(putText)等,說明如下:

--------------------------------------------------------------------------------------------------------------

1.line (InputOutputArray img, Point pt1, Point pt2, const Scalar &color, int thickness=1, int lineType=LINE_8, int shift=0)

img:輸入影像

pt1:起點座標

pt1:終點座標

color:顏色

thickness:預設厚度為1

lineType:預設8連結,選項有LINE_4 (4連結)、LINE_8(8連結) 、LINE_AA (反鋸齒)

shift:預設座標小數點數為0

--------------------------------------------------------------------------------------------------------------

2.circle (InputOutputArray img, Point center, int radius, const Scalar &color, int thickness=1, int lineType=LINE_8, int shift=0)

img:輸入影像

center:圓心座標

radius:半徑

color:顏色

thickness:預設厚度為1

lineType:預設8連結,選項有LINE_4 (4連結)、LINE_8(8連結) 、LINE_AA (反鋸齒)

shift:預設座標小數點數為0

--------------------------------------------------------------------------------------------------------------

3.rectangle (Mat &img, Rect rec, const Scalar &color, int thickness=1, int lineType=LINE_8, int shift=0)
rectangle (InputOutputArray img, Point pt1, Point pt2, const Scalar &color, int thickness=1, int lineType=LINE_8, int shift=0)

img:輸入影像

rect:矩形範圍(矩形左上角x座標,矩形左上角y座標,矩形寬度,矩形高度)

pt1:矩形左上角座標

pt2:矩形右下角座標

color:顏色

thickness:預設厚度為1

lineType:預設8連結,選項有LINE_4 (4連結)、LINE_8(8連結) 、LINE_AA (反鋸齒)

shift:預設座標小數點數為0

除此之外,如果想要繪製旋轉的矩形,則是定義RotateRect,並搭配line一條一條繪製,可參考下列程式碼

--------------------------------------------------------------------------------------------------------------

4.ellipse (InputOutputArray img, const RotatedRect &box, const Scalar &color, int thickness=1, int lineType=LINE_8)
ellipse (InputOutputArray img, Point center, Size axes, double angle, double startAngle, double endAngle, const Scalar &color, int thickness=1, int lineType=LINE_8, int shift=0)

img:輸入影像

rotatedRect:旋轉矩形範圍(Point2f圓心座標, Size2f 矩形寬高,旋轉角度)

center:圓心座標

axes:橢圓長短軸長度的一半

angle:橢圓旋轉角度(請參考下圖,3.2.0版本圖片似乎是錯誤的,所以選3.3.0的圖片說明,註1)

startAngle:繪製起始角度(請參考下圖,註1)

endAngle:繪製結束角度(請參考下圖,註1)

color:顏色

thickness:預設厚度為1

lineType:預設8連結,選項有LINE_4 (4連結)、LINE_8(8連結) 、LINE_AA (反鋸齒)

shift:預設座標小數點數為0



--------------------------------------------------------------------------------------------------------------

5.polylines (InputOutputArray img, InputArrayOfArrays pts, bool isClosed, const Scalar &color, int thickness=1, int lineType=LINE_8, int shift=0)

img:輸入影像

pts:點座標

isClosed:是否為封閉輪廓

color:顏色

thickness:預設厚度為1

lineType:預設8連結,選項有LINE_4 (4連結)、LINE_8(8連結) 、LINE_AA (反鋸齒)

shift:預設座標小數點數為0

--------------------------------------------------------------------------------------------------------------

6.putText (InputOutputArray img, const String &text, Point org, int fontFace, double fontScale, Scalar color, int thickness=1, int lineType=LINE_8, bool bottomLeftOrigin=false)

img:輸入影像

text:欲輸入文字內容

org:文字左下角座標

fontFace:文字類型(請參考下圖,註2)

fontScale:文字大小

color:顏色

thickness:預設厚度為1

lineType:預設8連結,選項有LINE_4 (4連結)、LINE_8(8連結) 、LINE_AA (反鋸齒)

shift:預設座標小數點數為0


--------------------------------------------------------------------------------------------------------------

程式碼如下:

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(){

	Mat SrcImg(500, 500, CV_8UC3, Scalar(0, 0, 0));
	Point StartPt = Point(25, 25), EndPt = Point(475, 475);
	Point TextPt = Point(250, 65), CenterPt = Point(250, 250);
	Size2f RotateSize = Size2f(200, 60);
	int Thickness = 3;

	//line function
	line(SrcImg, StartPt, EndPt, Scalar(0, 255, 0), Thickness, 8, 0);

	//circle function
	circle(SrcImg, CenterPt, 30, Scalar(0, 255, 255), -1, 8, 0);

	//rectangle function
	Rect NormalRect = Rect(25, 25, 100, 100);
	rectangle(SrcImg, NormalRect, Scalar(255, 0, 0), Thickness, 8, 0);
	rectangle(SrcImg, Point(375, 375), EndPt, Scalar(255, 0, 0), Thickness, 8, 0);

	//rotatedRect function
	RotatedRect RotateRect = RotatedRect(CenterPt, RotateSize, 45);
	Point2f Vertices[4];
	RotateRect.points(Vertices);
	for (int i = 0; i < 4; i++)
		line(SrcImg, Vertices[i], Vertices[(i + 1) % 4], Scalar(255, 255, 0), Thickness, 8, 0);

	//ellipse function
	ellipse(SrcImg, CenterPt, Size(RotateSize.width / 2, RotateSize.height / 2), -45, 0, 360, Scalar(255, 0, 255), Thickness, 8, 0);
	ellipse(SrcImg, RotateRect, Scalar(255, 0, 255), Thickness, 8);

	//polylines function
	vector polygons;
	polygons.push_back(Point(25, 125));
	polygons.push_back(Point(125, 125));
	polygons.push_back(Point(125, 375));
	polygons.push_back(Point(375, 375));
	polygons.push_back(Point(375, 475));
	polygons.push_back(Point(25, 475));
	polylines(SrcImg, polygons, true, Scalar(255, 150, 150), Thickness, 8, 0);

	//putText function
	putText(SrcImg, "OpenCV", TextPt, FONT_HERSHEY_COMPLEX, 1.5, Scalar(0, 0, 255), Thickness, 8, false);
	circle(SrcImg, TextPt, 1, Scalar(0, 0, 255), Thickness, 8, 0);

	imshow("SrcImg", SrcImg);

	waitKey(0);
	return 0;
}
結果圖:





備註(參考資料):
1.https://docs.opencv.org/3.3.0/d6/d6e/group__imgproc__draw.html
2.https://docs.opencv.org/3.3.0/d0/de1/group__core.html#ga0f9314ea6e35f99bb23f29567fc16e11

2020年9月14日 星期一

南投清境農場2日遊(小瑞士花園、高空步道、綿羊表演)

最近剛好有機會跟家人趁休假,規劃去南投清境玩,預定行程如下:

Day1

家->熱情果大本舖->小瑞士花園->櫻花步道->紙箱王->柳杉步道->飯店

Day2

飯店->清境農場->高空步道->綿羊表演->家

-------------------Go--------------------------------Go---------------

第一站

家->熱情果大本舖->小瑞士花園->櫻花步道->紙箱王->柳杉步道->飯店

純粹是想買百香果行程,因為埔里盛產水果之一就是百香果,且聽說有工廠提供遊客現場採百香果活動,有興趣的朋友們可以自己查查~ 這邊就不詳談了

第二站

家->熱情果大本舖->小瑞士花園->櫻花步道->紙箱王->柳杉步道->飯店

這邊建議可以先將車子停在清境第一停車場,因為小瑞士花園剛好就在旁邊,當然也可以直接停在小瑞士花園內的停車場,但感覺數量比較少




停車場附近有星巴克、7-11、摩斯等店家,中午非常適合在這邊吃飯,可以直接遙望中央山脈的美景,還有說明圖可以參考呢,不過因為是在山上,所以蒼蠅比較多,建議可以找室內餐廳吃比較好





還可以跟OPEN小將拍照一下呢~原來這邊海拔1743公尺


好的,回到重點小瑞士公園,小瑞士公園位於停車場的另一方,甚至有許多家庭來這邊露營呢,有興趣的人可以找一下



售票口前的小廣場,賣的東西不多,但人潮依舊眾多


票價參考,全票平日NT$ 120 元;假日150元、優惠票平日 NT$ 90 元;假日120元、愛心票不分平假日60元、保險票不分平假日20元


歡迎來到小瑞士公園


不小心拍成過曝的兩頭羊


我個人覺得裡面的特色主要有兩個,一個就是花園,二來就是會跟音樂搭配的噴水湖畔,但老實說花的數量蠻少的,不知道是不是剛好在整修,然後人造景也很有限




四處都有魚飼料販賣機,可以嘗試餵看看,會發現魚兒總是瘋狂的飛奔過來,不知道是餓了多久,哈哈


還有遇到野貓吃魚的畫面,不過沒拍到,可惡!!
聽說這隻貓在這邊吃了很多回,有夠誇張的


裡面還有幾隻很漂亮的黑天鵝,我的老天鵝呀

還有設置給情侶們專用的拍照區


一起來敲鐘,越敲越幸福




整體來說,我覺得CP值有點不夠,因為你能看到的東西有限,而票價120元(優待票90元),當然還是因人而異啦。

第三站

家->熱情果大本舖->小瑞士花園->櫻花步道->紙箱王->柳杉步道->飯店

在前往紙箱王之前呢,跟大家快速說一下,在清境這邊有9條步道,各有各的特色,如果不想要上去又要一直找車位,可以考慮車子停在第一停車場,並用步行的方式走櫻花步道到紙箱王後,再經由柳杉步道回到停車場,此外,個人覺得步步高升步道很漂亮,但沒時間走到,下次有機會走看看。

不過因為櫻花步道走起來有點普通,所以在這邊就沒有特別放照片說明了~

到了紙箱王喔,在門口就會看到超多用紙箱做的藝術品,然後很多家庭會帶小朋友來逛








第四站

家->熱情果大本舖->小瑞士花園->櫻花步道->紙箱王->柳杉步道->飯店



還能看到小瑞士露營的地方呢~



第五站

家->熱情果大本舖->小瑞士花園->櫻花步道->紙箱王->柳杉步道->飯店

這次沒有刻意拍飯店的內容,所以就不多作介紹了~


馬上又是第二天的開始,今天就是直接殺去清境農場了

第一站

飯店->高空步道->清境農場->綿羊表演->家

這一天天氣算還不錯,但記得要擦防曬或準備放曬的衣物,免得回家變成小黑人,走在高空步道感覺真的很不錯,可以眺望遠方,彷彿可以摸到雲層般







沿途還能看到很多羊群在底下吃草,吃得真是開心




不時會有雲(還是霧??)包覆步道,瞬間讓人以為天氣變糟了呢





美美的


第二站

飯店->高空步道->清境農場->綿羊表演->家
在來回走完完高空步道之後,我們從清境的南端票口進入閒晃~


這邊跟小瑞士一樣,可以買草飼料親手餵羊妹妹,不過走在路上要隨時注意,不要誤踩羊妹妹的便便,哈哈



一堆情侶或朋友們在草地上瘋狂拍照




第三站

飯店->高空步道->清境農場->綿羊表演->家

到了下午就是清淨裡面最代表的綿羊表演了,太陽很熱情使得大家紛紛拿出雨傘來遮羞,但還是要小心不要擋到後面觀看的人



人真的超多的拉~~




主持人講話很好笑,時不時的還會帶到時事,搞得現場民眾哈哈大笑



最後不免俗的跑到門口拍照一下



總結
1.出遊要認真擦防曬,尤其是在走高空步道的時候,因為缺乏遮蔽物所以更需要保護自己
2.步步高升步道感覺很值得去,這次沒有走到有點可惜,下次一定走看看