如何簡要利用單鏡頭計算前方車輛距離

更新於 2024/05/20閱讀時間約 34 分鐘

  如果要計算前方物體的距離,會需要應用到世界座標轉換的方法,不過本文提供快速的計算方式,不考慮相機校正的部分,主要會解釋單應性矩陣的計算原理,但要精確還是要進行相機校正。如果如果想知道詳細相機校正的內容,可以參考此文章。

  大致上的步驟,會利用實際世界的四個位置與其對應圖像上的四個位置去計算出單應性矩陣,也就是將像素座標轉世界座標,進而去推算前方車輛與我們車子的距離。



1.什麼是單應性矩陣?

  單應性矩陣(Homography)是計算機視覺和影像處理中的一個重要概念,主要用來描述兩個圖像平面之間的變換關係。它可以應用在多種情境中,例如圖像拼接、立體視覺、相機校正以及車前距離評估等。可以用透視變換(Perspective Transform)表達大致概念。

圖一 透視示意圖

圖一 透視示意圖


  在日常生活中有一個常見的應用。在疫情情間遠距教學時,手邊不一定有複印機,這時就可以用透視變換的方法,掃描要上傳作業的作業。

圖二 日常應用示意圖

圖二 日常應用示意圖


2.如何計算單應性矩陣?

  計算的公式如式2-1。(u,v)是我們在實際是世界中的座標,(x,y)是圖像上的像素座標,h矩陣則是單應性矩陣。

raw-image

  從一般的情況分析,每一組(x,y)匹配到(u,v),都有等式2-2成立。拆解式2-2,可以得到式2-3與式2-4。

raw-image
raw-image
raw-image

式2-3與式2-4可以進一步變換為:

raw-image

  由於有八個單應性矩陣的參數要求解,而每組只能產生一對方程式,所以總共需要四組點,才夠求出單應性矩陣h的唯一解。最後可表示成式2-7。

raw-image

  可以用DLT(Direct Linear Transform)之類的方式求解,或者利用opencv,opencv有函式可以使用。

C++: Mat findHomography(InputArray srcPoints, InputArray dstPoints, int method=0, double ransacReprojThreshold=3, OutputArray mask=noArray() )
Python: cv2.findHomography(srcPoints, dstPoints[, method[, ransacReprojThreshold[, mask]]]) → retval, mask

3.實際的校正方式  

  在計算前方車輛距離時,因為是用地面上的距離去評估,所以我們使Z軸的值為一個定值。因此雖然真實世界是3D,但計算上只需要X軸與Y軸上的值。


  我們以公尺(m)為單位,去測量現實世界的標定位置。如圖三所示,以圖片中間為原點,向左右延伸2公尺。前後距離以3公尺為起始位置開始標定,最後延伸到60公尺的位置,同時也是計算車前距離的最遠極限。

圖三 現實世界標定示意圖

圖三 現實世界標定示意圖


程式

計算單應性矩陣程式:

#include <iostream>
#include <opencv2/opencv.hpp>
#include <stdio.h>
#include <fstream>
#include <string.h>

//width height
//像素座標
int image3[2][2] = {{212, 570},{1022, 633}};
int image5[2][2] = {{338, 499},{904, 539}};
int image10[2][2] = {{465, 432},{789, 453}};
int image20[2][2] = {{543, 388},{717, 399}};
int image30[2][2] = {{574, 373},{694, 381}};
int image40[2][2] = {{591, 365},{682, 371}};
int image50[2][2] = {{601, 361},{675, 367}};

//width height
//世界座標
int world3[2][2] = {{-200, 300},{200, 300}};
int world5[2][2] = {{-200, 500},{200, 500}};
int world10[2][2] = {{-200, 1000},{200, 1000}};
int world20[2][2] = {{-200, 2000},{200, 2000}};
int world30[2][2] = {{-200, 3000},{200, 3000}};
int world40[2][2] = {{-200, 4000},{200, 4000}};
int world50[2][2] = {{-200, 5000},{200, 5000}};

//單應性矩陣
cv::Mat H_3(3, 3, CV_64FC1);
cv::Mat H_inv_3(3, 3, CV_64FC1);
cv::Mat H_5(3, 3, CV_64FC1);
cv::Mat H_inv_5(3, 3, CV_64FC1);
cv::Mat H_10(3, 3, CV_64FC1);
cv::Mat H_inv_10(3, 3, CV_64FC1);
cv::Mat H_20(3, 3, CV_64FC1);
cv::Mat H_inv_20(3, 3, CV_64FC1);
cv::Mat H_30(3, 3, CV_64FC1);
cv::Mat H_inv_30(3, 3, CV_64FC1);
cv::Mat H_40(3, 3, CV_64FC1);
cv::Mat H_inv_40(3, 3, CV_64FC1);

char ctfct_3, ctfct_5, ctfct_10, ctfct_20, ctfct_30, ctfct_40, ctfct_inv_3, ctfct_inv_5, ctfct_inv_10, ctfct_inv_20, ctfct_inv_30, ctfct_inv_40;

//讀入二進制文件
int readbin(int num, bool inv) {

// 創建一個用來指向3X3 cv::Mat的指標
double* ptr;

std::string file_name;

if(!inv){
file_name = "Homography_" + std::to_string(num) + ".bin";
switch(num){
case 3:
ptr = H_3.ptr<double>();
break;
case 5:
ptr = H_5.ptr<double>();
break;
case 10:
ptr = H_10.ptr<double>();
break;
case 20:
ptr = H_20.ptr<double>();
break;
case 30:
ptr = H_30.ptr<double>();
break;
case 40:
ptr = H_40.ptr<double>();
break;
default:
break;
}
}
else{
file_name = "Homography_inv_" + std::to_string(num)+ ".bin";
switch(num){
case 3:
ptr = H_inv_3.ptr<double>();
break;
case 5:
ptr = H_inv_5.ptr<double>();
break;
case 10:
ptr = H_inv_10.ptr<double>();
break;
case 20:
ptr = H_inv_20.ptr<double>();
break;
case 30:
ptr = H_inv_30.ptr<double>();
break;
case 40:
ptr = H_inv_40.ptr<double>();
break;
default:
break;
}

}

//打開二進制文件
std::ifstream inFile(file_name, std::ios::in | std::ios::binary);

//檢查文件是否打開
if (!inFile.is_open()) {
std::cerr << "Failed to open the file." << std::endl;
return 1;
}

//獲取文件大小
inFile.seekg(0, std::ios::end);
int fileSize = inFile.tellg();
inFile.seekg(0, std::ios::beg);

//讀取數據
double* data = new double[fileSize / sizeof(double)];
inFile.read(reinterpret_cast<char*>(data), fileSize);

//關閉文件
inFile.close();




//將儲存的數據填充到cv::Mat變數中
std::memcpy(ptr, data, fileSize);

//釋放記憶體
delete[] data;
return 0;

}

//寫出二進制文件
int writeBin(std::vector<cv::Point2f> src_points,std::vector<cv::Point2f> dst_points ,std::string num ,bool inv) {
cv::Mat H = cv::findHomography(src_points, dst_points);
cv::Mat H_inv;
cv::invert(H, H_inv);
std::string file_name;

//將矩陣的數據寫入文件
const double* dataPtr ;

if(inv){
file_name = "Homography_inv_" + num + ".bin";
//打開二進制文件
std::ofstream outFile(file_name, std::ios::out | std::ios::binary);

//檢查文件是否打開
if (!outFile.is_open()) {
std::cerr << "Failed to open the file." << std::endl;
return 1;
}
dataPtr = reinterpret_cast<const double*>(H_inv.data);
outFile.write(reinterpret_cast<const char*>(dataPtr), H_inv.rows * H_inv.cols * sizeof(double));

//關閉文件
outFile.close();
}
else{
file_name = "Homography_" + num + ".bin";
//打開二進制文件
std::ofstream outFile(file_name, std::ios::out | std::ios::binary);


//檢查文件是否打開
if (!outFile.is_open()) {
std::cerr << "Failed to open the file." << std::endl;
return 1;
}
dataPtr = reinterpret_cast<const double*>(H.data);
outFile.write(reinterpret_cast<const char*>(dataPtr), H.rows * H.cols * sizeof(double));

//關閉文件
outFile.close();
}

std::cout << file_name << " has been written" << std::endl;

return 0;

}


//單應性矩陣讀入(false)與寫出(true)
void Homography(bool mode){
if(mode){
std::vector<cv::Point2f> imagePoint3 = {
cv::Point2f(image3[0][0], image3[0][1]),
cv::Point2f(image3[1][0], image3[1][1]),
cv::Point2f(image5[0][0], image5[0][1]),
cv::Point2f(image5[1][0], image5[1][1])
};

std::vector<cv::Point2f> imagePoint5 = {
cv::Point2f(image5[0][0], image5[0][1]),
cv::Point2f(image5[1][0], image5[1][1]),
cv::Point2f(image10[0][0], image10[0][1]),
cv::Point2f(image10[1][0], image10[1][1])
};

std::vector<cv::Point2f> imagePoint10 = {
cv::Point2f(image10[0][0], image10[0][1]),
cv::Point2f(image10[1][0], image10[1][1]),
cv::Point2f(image20[0][0], image20[0][1]),
cv::Point2f(image20[1][0], image20[1][1])
};

std::vector<cv::Point2f> imagePoint20 = {
cv::Point2f(image20[0][0], image20[0][1]),
cv::Point2f(image20[1][0], image20[1][1]),
cv::Point2f(image30[0][0], image30[0][1]),
cv::Point2f(image30[1][0], image30[1][1])
};

std::vector<cv::Point2f> imagePoint30 = {
cv::Point2f(image30[0][0], image30[0][1]),
cv::Point2f(image30[1][0], image30[1][1]),
cv::Point2f(image40[0][0], image40[0][1]),
cv::Point2f(image40[1][0], image40[1][1])
};

std::vector<cv::Point2f> imagePoint40 = {
cv::Point2f(image40[0][0], image40[0][1]),
cv::Point2f(image40[1][0], image40[1][1]),
cv::Point2f(image50[0][0], image50[0][1]),
cv::Point2f(image50[1][0], image50[1][1])
};

std::vector<cv::Point2f> worldPoint3 = {
cv::Point2f(world3[0][0], world3[0][1]),
cv::Point2f(world3[1][0], world3[1][1]),
cv::Point2f(world5[0][0], world5[0][1]),
cv::Point2f(world5[1][0], world5[1][1])
};

std::vector<cv::Point2f> worldPoint5 = {
cv::Point2f(world5[0][0], world5[0][1]),
cv::Point2f(world5[1][0], world5[1][1]),
cv::Point2f(world10[0][0], world10[0][1]),
cv::Point2f(world10[1][0], world10[1][1])
};

std::vector<cv::Point2f> worldPoint10 = {
cv::Point2f(world10[0][0], world10[0][1]),
cv::Point2f(world10[1][0], world10[1][1]),
cv::Point2f(world20[0][0], world20[0][1]),
cv::Point2f(world20[1][0], world20[1][1])

};

std::vector<cv::Point2f> worldPoint20 = {
cv::Point2f(world20[0][0], world20[0][1]),
cv::Point2f(world20[1][0], world20[1][1]),
cv::Point2f(world30[0][0], world30[0][1]),
cv::Point2f(world30[1][0], world30[1][1])
};

std::vector<cv::Point2f> worldPoint30 = {
cv::Point2f(world30[0][0], world30[0][1]),
cv::Point2f(world30[1][0], world30[1][1]),
cv::Point2f(world40[0][0], world40[0][1]),
cv::Point2f(world40[1][0], world40[1][1])
};

std::vector<cv::Point2f> worldPoint40 = {
cv::Point2f(world40[0][0], world40[0][1]),
cv::Point2f(world40[1][0], world40[1][1]),
cv::Point2f(world50[0][0], world50[0][1]),
cv::Point2f(world50[1][0], world50[1][1])
};


writeBin(imagePoint3,worldPoint3,"3",false);
writeBin(imagePoint3,worldPoint3,"3",true);
writeBin(imagePoint5,worldPoint5,"5",false);
writeBin(imagePoint5,worldPoint5,"5",true);
writeBin(imagePoint10,worldPoint10,"10",false);
writeBin(imagePoint10,worldPoint10,"10",true);
writeBin(imagePoint20,worldPoint20,"20",false);
writeBin(imagePoint20,worldPoint20,"20",true);
writeBin(imagePoint30,worldPoint30,"30",false);
writeBin(imagePoint30,worldPoint30,"30",true);
writeBin(imagePoint40,worldPoint40,"40",false);
writeBin(imagePoint40,worldPoint40,"40",true);
}
else{
readbin(3, false);
readbin(3, true);
readbin(5, false);
readbin(5, true);
readbin(10, false);
readbin(10, true);
readbin(20, false);
readbin(20, true);
readbin(30, false);
readbin(30, true);
readbin(40, false);
readbin(40, true);
}

}



//驗證單應性矩陣的正確性的公式
char Certification_Homography_coordinate_transform(double x,double y, double u_org, double v_org,cv::Mat H, std::string num){
cv::Mat A = (cv::Mat_<double>(3, 1) << x, y, 1.f);
cv::Mat tmp = H * A;
tmp = tmp/tmp.at<double>(2, 0);
std::string file_name = "Homography_" + num + ".bin";
std::cout << "Matrix from " << file_name << ":"<< std::endl;
std::cout << H << std::endl;
std::cout << std::endl;
if(std::fabs(u_org - tmp.at<double>(0, 0)) > 1 || std::fabs(v_org - tmp.at<double>(1, 0)) > 1 ){
printf("%lf %lf\n",tmp.at<double>(0, 0),tmp.at<double>(1, 0));
return -1;
}
else
return 0;

}



//驗證單應性矩陣的正確性
int Certification(){
ctfct_3 = Certification_Homography_coordinate_transform(image5[0][0], image5[0][1],world5[0][0], world5[0][1], H_3, "3");
ctfct_5 = Certification_Homography_coordinate_transform(image5[0][0], image5[0][1],world5[0][0], world5[0][1], H_5, "5");
ctfct_10 = Certification_Homography_coordinate_transform(image20[0][0], image20[0][1],world20[0][0], world20[0][1], H_10 ,"10");
ctfct_20 = Certification_Homography_coordinate_transform(image20[0][0], image20[0][1],world20[0][0], world20[0][1], H_20 ,"20");
ctfct_30 = Certification_Homography_coordinate_transform(image40[0][0], image40[0][1],world40[0][0], world40[0][1], H_30 ,"30");
ctfct_40 = Certification_Homography_coordinate_transform(image40[0][0], image40[0][1],world40[0][0], world40[0][1], H_40 ,"40");

ctfct_inv_3 = Certification_Homography_coordinate_transform(world5[0][0], world5[0][1],image5[0][0], image5[0][1], H_inv_3 ,"inv_3");
ctfct_inv_5 = Certification_Homography_coordinate_transform(world5[0][0], world5[0][1],image5[0][0], image5[0][1], H_inv_5 ,"inv_5");
ctfct_inv_10 = Certification_Homography_coordinate_transform(world20[0][0], world20[0][1],image20[0][0], image20[0][1], H_inv_10 ,"inv_10");
ctfct_inv_20 = Certification_Homography_coordinate_transform(world20[0][0], world20[0][1],image20[0][0], image20[0][1], H_inv_20 ,"inv_20");
ctfct_inv_30 = Certification_Homography_coordinate_transform(world40[0][0], world40[0][1],image40[0][0], image40[0][1], H_inv_30 ,"inv_30");
ctfct_inv_40 = Certification_Homography_coordinate_transform(world40[0][0], world40[0][1],image40[0][0], image40[0][1], H_inv_40 ,"inv_40");
//printf("%d\n",ctfct_3);


if(ctfct_3 != 0){
printf("H_3 wrong!\n");
return -1;
}
else if(ctfct_5 != 0){
printf("H_5 wrong!\n");
return -1;
}
else if(ctfct_10 !=0){
printf("H_10 wrong!\n");
return -1;
}
else if(ctfct_20 != 0){
printf("H_20 wrong!\n");
return -1;
}
else if(ctfct_30 != 0){
printf("H_30 wrong!\n");
return -1;
}
else if(ctfct_40 != 0){
printf("H_40 wrong!\n");
return -1;
}
else if(ctfct_inv_3 != 0){
printf("H_inv_3 wrong!\n");
return -1;
}
else if(ctfct_inv_5 != 0){
printf("H_inv_5 wrong!\n");
return -1;
}
else if(ctfct_inv_10 != 0){
printf("H_inv_10 wrong!\n");
return -1;
}
else if(ctfct_inv_20 != 0){
printf("H_inv_20 wrong!\n");
return -1;
}
else if(ctfct_inv_30 != 0){
printf("H_inv_30 wrong!\n");
return -1;
}
else if(ctfct_inv_40 != 0){
printf("H_inv_40 wrong!\n");
return -1;
}
else{
printf("Certification OK!!!\n");
}
return 0;

}


void Homography_coordinate_transform(double srcX,double srcY, double &dstX, double &dstY,cv::Mat H){
cv::Mat A = (cv::Mat_<double>(3, 1) << srcX, srcY, 1.f);
cv::Mat tmp = H * A;
tmp = tmp/tmp.at<double>(2, 0);

dstX = tmp.at<double>(0, 0);
dstY = tmp.at<double>(1, 0);

}

int main(int argc, char **argv) {

if(argc < 2){
printf("you need to selet mode, '-c' or '-C'\n");
return -1;
}

//計算單應性矩陣並寫出
if (std::string(argv[1]) == "-w"){
Homography(true);
return 0;
}
//驗證單應性矩陣的正確性
else if (std::string(argv[1]) == "-C"){
//讀取單應性矩陣數據
Homography(false);
//驗證單應性矩陣
Certification();
return 0;
}
else{
printf("'-d' or '-w' or '-C' \n");
return -1;
}

return 0;

}


  總的來說,在現實世界標定四個點之後,比對圖像上的像素座標,計算出單應性矩陣,之後就可以用這組參數去換算距離。之後利用YOLO v8之類的物件檢測模型,去檢測前方物件,以物件框最下方中間位置為像素座標,最後轉換成距離。如圖四所示。

圖四 距離計算事例

圖四 距離計算事例



後記

  剛好有機會要教實驗室的學弟,相關的原理,一時興起就整理整理成一篇文章。有空應該會再補上,精確的求法。



參考資料

鐘竣耀(2022)。應用於汽車遠距離物件辨識之超輕量深度學習網路架構設計與實現。﹝碩士論文。國立雲林科技大學﹞臺灣博碩士論文知識加值系統。 https://hdl.handle.net/11296/ewk4cc。


avatar-img
1會員
2內容數
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
黃賢的沙龍 的其他內容
簡介   I2C (Inter - Intertraed circuit)內部整合電路,顧名思義就是主要控制短距離的內部裝置,而不適用於長距離的傳輸,I2C有不同的速度模式通常都是雙向傳輸,如標準模式100 Kbit/s、低速模式 10 Kbit/s、快速模式400Kbit/s、高速模式3.4Mbi
簡介   I2C (Inter - Intertraed circuit)內部整合電路,顧名思義就是主要控制短距離的內部裝置,而不適用於長距離的傳輸,I2C有不同的速度模式通常都是雙向傳輸,如標準模式100 Kbit/s、低速模式 10 Kbit/s、快速模式400Kbit/s、高速模式3.4Mbi
你可能也想看
Google News 追蹤
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
吃藥、吃保健食品之後,這些日子我們得到了什麼?是真正的健康嗎?還是我們人類只是半遮半掩地假裝健康。如果能好起來,說真的,那麼我們吃藥、吃保健食品幹嘛。
Thumbnail
我在單身的最後一年,很認真地找書籍資料,想探索托單的奧秘!然後竟然就被我找到了,這本書簡直像是在呼喚我,於是我藉由書上的訊息,加上自身五年的努力與經驗,我整理出一套脫單守則(秘笈)!我使用這個秘笈後,僅花了兩個月就找到我的理想情人。
Thumbnail
更新日期:2021.12.06 作者:翁順法|FAHAHA 請大家思考、回憶一下:當需要上台簡報,拿到簡報題目後,你都是怎麼開始準備的? 要怎麼做呢?拿到簡報題目後,有三件事一定要先搞清楚: 1.搞清楚:講給誰聽? 2.搞清楚:目標為何? 3.搞清楚:時間多久?
Thumbnail
節目內容 把這件事看得太重了?把自己看得太重了? 做想做的事情,但存款減少很害怕? 能力差、作品爛是正常的? 馬上聽!(在你喜歡的平台上聆聽) YouTube版 Podcast版 看文字版 極簡生活問答 #12:自信不足、能力不夠,看著存款減少,要如何堅持創業? 來自聽眾的提問
Thumbnail
行政院的科技政委唐鳳在五月十九日公布簡訊實聯制,讓全民可以不用紙本進行實聯制,標榜一方面降低店家成本,另一方面保障民眾個資不必寫在紙上造成可能外洩的風險,更令人驚喜的是,發送的簡訊費用都由電信業者吸收,不必花納稅人一毛錢,可以說是既口罩預購系統後,科技政委為我們帶來最好的禮物,解決了疫情爆發初期的實
Thumbnail
2019年引起廣大上班族共鳴的日劇《我要準時下班》,探討了亞洲常見的「加班、高工時」的議題,劇中女主角東山結衣的中心理念──「準時下班」深切打中在高壓下的亞洲員工們。然而在劇中除了時間管理的概念之外,也額外探討了許多議題,其中一項正是全球都常見的因育兒所帶來的職場壓力問題。
Thumbnail
這幾年,我對於關注性別議題的熱情已經不如以往,不過今天在刷臉書的時候,意外看到了淡大這篇“公審文”,而這篇文章的內容,進一步引起了我的興趣:
Thumbnail
商業週刊最近的一則報導「中小企業帳上現金還能維持營運多久?34%的企業,只能維持一個月;33%的企業,可以維持兩個月;只有不到10%的公司,可以維持6個月」,而審查大人也開始要求全面檢視有「中國曝險」部位的客戶時,應該要如何準備起呢…
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
吃藥、吃保健食品之後,這些日子我們得到了什麼?是真正的健康嗎?還是我們人類只是半遮半掩地假裝健康。如果能好起來,說真的,那麼我們吃藥、吃保健食品幹嘛。
Thumbnail
我在單身的最後一年,很認真地找書籍資料,想探索托單的奧秘!然後竟然就被我找到了,這本書簡直像是在呼喚我,於是我藉由書上的訊息,加上自身五年的努力與經驗,我整理出一套脫單守則(秘笈)!我使用這個秘笈後,僅花了兩個月就找到我的理想情人。
Thumbnail
更新日期:2021.12.06 作者:翁順法|FAHAHA 請大家思考、回憶一下:當需要上台簡報,拿到簡報題目後,你都是怎麼開始準備的? 要怎麼做呢?拿到簡報題目後,有三件事一定要先搞清楚: 1.搞清楚:講給誰聽? 2.搞清楚:目標為何? 3.搞清楚:時間多久?
Thumbnail
節目內容 把這件事看得太重了?把自己看得太重了? 做想做的事情,但存款減少很害怕? 能力差、作品爛是正常的? 馬上聽!(在你喜歡的平台上聆聽) YouTube版 Podcast版 看文字版 極簡生活問答 #12:自信不足、能力不夠,看著存款減少,要如何堅持創業? 來自聽眾的提問
Thumbnail
行政院的科技政委唐鳳在五月十九日公布簡訊實聯制,讓全民可以不用紙本進行實聯制,標榜一方面降低店家成本,另一方面保障民眾個資不必寫在紙上造成可能外洩的風險,更令人驚喜的是,發送的簡訊費用都由電信業者吸收,不必花納稅人一毛錢,可以說是既口罩預購系統後,科技政委為我們帶來最好的禮物,解決了疫情爆發初期的實
Thumbnail
2019年引起廣大上班族共鳴的日劇《我要準時下班》,探討了亞洲常見的「加班、高工時」的議題,劇中女主角東山結衣的中心理念──「準時下班」深切打中在高壓下的亞洲員工們。然而在劇中除了時間管理的概念之外,也額外探討了許多議題,其中一項正是全球都常見的因育兒所帶來的職場壓力問題。
Thumbnail
這幾年,我對於關注性別議題的熱情已經不如以往,不過今天在刷臉書的時候,意外看到了淡大這篇“公審文”,而這篇文章的內容,進一步引起了我的興趣:
Thumbnail
商業週刊最近的一則報導「中小企業帳上現金還能維持營運多久?34%的企業,只能維持一個月;33%的企業,可以維持兩個月;只有不到10%的公司,可以維持6個月」,而審查大人也開始要求全面檢視有「中國曝險」部位的客戶時,應該要如何準備起呢…