團隊——天亮說晚安
視頻解碼模塊
//生成遮罩 std::vector<cv::Mat> mask(8); for(int i = 0; i < 8; i++) { mask[i] = ... }
目前,項目讀取編碼的手段是將圖像根據(jù)定位點將圖像尺寸還原奢米,用遮罩捕獲目標區(qū)域炕泳,所以需要先初始化std::vector<mask>
。
std::vector<cv::Mat>video_pro; for (int k = 0; k < frames ; k++) { //彩色轉(zhuǎn)灰度 cv::cvtColor(src, src, cv::COLOR_RGB2GRAY); //閾值 int SRC_THRESH_LOW = this->THRESH; cv::threshold(src, src, SRC_THRESH_LOW, 255, cv::THRESH_BINARY); //Canny邊緣檢測 cv::Mat src_gray = src; cv::Mat canny_output; cv::Canny(src_gray, canny_output, 200, 255); //輪廓提取 std::vector<std::vector<cv::Point> > contours; std::vector<cv::Vec4i> hierarchy; cv::findContours(canny_output, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); if(contours.size()<1)//若提取失敗源哩,跳過此幀 continue; //提取最大矩形知态,即屏幕現(xiàn)實范圍 cv::Rect poly_rect_max=cv::boundingRect(contours[0]); for (int i = 1; i < contours.size(); i++) { if(poly_rect_max.area()<cv::boundingRect(contours[i]).area()) poly_rect_max=cv::boundingRect(contours[i]); } //ROI坐標生成 tl=topleft,br=bottomright int tl_x = poly_rect_max.tl().x; int br_x = poly_rect_max.br().x; int tl_y = poly_rect_max.tl().y; int br_y = poly_rect_max.br().y; //ROI區(qū)域生成 cv::Rect rect(tl_x, tl_y, br_x - tl_x, br_y - tl_y); cv::Mat srcRoi=cv::Mat::zeros(src.size(), CV_8UC1); srcRoi=src(rect); cv::resize(srcRoi, srcRoi, RSFRAME); //存入預降噪vector保存 video_pro.push_back(srcRoi); }
這一步是降噪得以實現(xiàn)的核心,通過電腦屏幕底色的白色倒得,將目標區(qū)域初步的提取出來,降低外部的噪聲夭禽。cv::cvtColor()
霞掺、cv::threshold()
實現(xiàn)初步的噪聲去除,cv::Canny()
讹躯、cv::findContours
提取出ROI區(qū)域和相應的輪廓菩彬,將輪廓外的區(qū)域全部消除。此步本應該為可選項潮梯,但是該段邏輯在Release 1.0
的版本本是被強制執(zhí)行的骗灶,會出現(xiàn)用戶很精準的沒有錄入噪聲,卻讀取失敗的情況秉馏,Beta 2.0
版本中擬予以優(yōu)化耙旦。
std::vector<cv::Mat> video_diff; for(int i=1;i<video_pro.size();i++){ //差值 cv::Mat st=video_pro[i]-video_pro[i-1]; //形態(tài)學開 cv::Mat kernelsrc = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(23, 23), cv::Point(-1, -1)); cv::morphologyEx(st, st, cv::MORPH_OPEN, kernelsrc); //存入差值vector video_diff.push_back(st); }
使用幀差法有兩個目的,第一個是進一步去除噪聲萝究。第二目的是捕獲關鍵幀免都。這里的關鍵幀是指清晰成像的,帶有信息的圖像帆竹。實際幀之間做差時绕娘,有三種情況:
-
[i-1]
幀為空白幀,[i]
幀為關鍵幀(無論清晰與否栽连,下同)——這時险领,由于減去白色的緣故,[i]
幀信息全部丟失秒紧。 -
[i-1]
幀為關鍵幀绢陌,[i]
也為關鍵幀——做差后,[i]
會刪去大部分信息噩茄,留下一些無用的噪點下面。 -
[i-1]
幀為關鍵幀,[i]
空白幀——這時绩聘,空白幀[i]
會因為剪掉了信息區(qū)域外的黑色而保留信息區(qū)域沥割。
這個時候耗啦,理論上,第一種情況產(chǎn)生黑色空白幀直接被忽略机杜,第二種情況在形態(tài)學開操作cv::morphologyEx()
的處理下帜讲,退化為黑色空白幀,只有第三種情況產(chǎn)生的幀會被捕獲椒拗。但在實際解算時似将,由于灰階響應的緣故,閾值后的信息會出現(xiàn)不完整的情況蚀苛。這種情況會直接導致程序不正常退出或者OpenCV斷言錯誤在验,實際UI顯示為卡66%的進度。
Relese 1.0
版本中這個現(xiàn)象尤為嚴重堵未,因此為Relese 2.0
版本發(fā)布前的重中之重腋舌。
for(int i=0;i<video_diff.size();i++){ //過濾低信息圖片 if(cv::countNonZero(video_diff[i])==0) continue; cv::Mat read=video_diff[i]; //Canny邊緣查找 cv::Mat src_gray = read; cv::Mat canny_output; cv::Canny(src_gray, canny_output, 200, 255); //輪廓查找 std::vector<std::vector<cv::Point> > contours; std::vector<cv::Vec4i> hierarchy; cv::findContours(canny_output, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); if(contours.size()<=2) continue; //從輪廓生成最小矩形 std::vector<cv::Rect>poly_rect(contours.size()); for (int i = 0; i < contours.size(); i++) { poly_rect[i] = cv::boundingRect(contours[i]); } //矩形輪廓x坐標整理 for (int i = poly_rect.size(); i > 0; i--) { for (int j = 0; j < i - 1; j++) { if (poly_rect[j].tl().x > poly_rect[j + 1].tl().x) swap(poly_rect[j], poly_rect[j + 1]); } } //矩形輪廓y坐標整理 for (int i = 0; i + 1 < poly_rect.size(); i += 2) { if (poly_rect[i].tl().y > poly_rect[i + 1].tl().y) swap(poly_rect[i], poly_rect[i + 1]); } //ROI坐標生成 tl=topleft,br=bottomright int tl_x = poly_rect[0].tl().x; int br_x = poly_rect[poly_rect.size() - 1].br().x; int tl_y = poly_rect[0].tl().y; int br_y = poly_rect[poly_rect.size() - 1].br().y; if(tl_x&&br_x&&tl_y&&br_y==0) continue; //矩形輪廓重建 cv::Mat rscrc = cv::Mat::zeros(canny_output.size(), CV_8UC1); for (int i = 0; i < poly_rect.size(); i++) { cv::rectangle(rscrc, poly_rect[i], WHITE, cv::FILLED); } //ROI區(qū)域生成 cv::Rect rect(tl_x, tl_y, br_x - tl_x, br_y - tl_y); cv::Mat srcRoi=cv::Mat::zeros(rscrc.size(), CV_8UC1); srcRoi=rscrc(rect); cv::resize(srcRoi, srcRoi, RSFRAME); //解碼 char c=0; for (int i = 7; i >= 0; i--) { //緩存幀 cv::Mat p = cv::Mat::zeros(RSFRAME, CV_8UC1); //添加遮罩 p = srcRoi-mask[i]; //譯碼 if (cv::countNonZero(p) >500) c |= 0x01; if (i != 0) c <<= 1; } }
最后解碼階段,相當于第一步的復現(xiàn)渗蟹,沒有新的內(nèi)容块饺,最后的循環(huán)實現(xiàn)了解碼,相當于編碼的逆過程雌芽。