傳統方式的圖像超像素常見的方式就是基於立方插值跟金字塔重建。OpenCV中對這兩種方式均有實現,低像素圖像在紋理細節方面很難恢復,從低像素圖像到高像素圖像是典型的一對多映射,若是找到一種好的映射關係能夠儘量多的恢復或者保留圖像紋理細節是圖像超像素重建的難點之一,傳統方式多數都是基於可推導的模型實現。而基於深度學習的超像素從新方式過程未知可是結果優於傳統方式。在深度學習方式的超像素重建中,對低像素圖像採樣大感覺野來獲取更多的紋理特徵信息。OpenVINO中提供的單張圖像超像素網絡參考了下面這篇文章
https://arxiv.org/pdf/1807.06779.pdfhtml
該網絡模型主要分爲兩個部分網絡
經過兩個網絡的的輸出相乘,還能夠獲得高分辨率圖像的殘差。特徵重建網絡主要包括三個部分。卷積層實現特徵提取,卷積層採樣大感覺野來獲得更多紋理細節;多個DenseRes 疊加模塊,級聯DenseRes可讓網絡更深,效果更好;一個亞像素卷積層做爲上採樣模塊。注意力生成網絡部分,用來恢復小的紋理細節,如圖像的邊緣與形狀,網絡能夠準肯定位到細節特徵,而後進行相對提高,注意力特徵網絡設計受到UNet網絡架構的啓發。完整的模型結構以下:
一個更簡介的網絡結構以下:
其中LR表示低分辨率圖像、HR表示高分辨率圖像,Bicubic表示雙立方插值上採樣。架構
OpenVINO提供的模型是在這個模型基礎上進行簡化,計算量更低,速度更快。從上面的模型結構知道,模型有兩個輸入部分,分別是輸入的低分辨率圖像與雙立方上採樣的圖像ide
LR的輸入:[1x3x270x480]
雙立方採樣:[1x3x1080x1920]
三通道順序是:BGR學習
模型的輸出測試
輸出層是一個blob對象,格式爲[1x3x1080x1920]設計
首先須要加載網絡模型,獲取可執行網絡,而後設置輸入與輸出的數據格式與數據精度,這部分的代碼以下:code
// 加載檢測模型 CNNNetReader network_reader; network_reader.ReadNetwork(model_xml); network_reader.ReadWeights(model_bin); // 請求網絡輸入與輸出信息 auto network = network_reader.getNetwork(); InferenceEngine::InputsDataMap input_info(network.getInputsInfo()); InferenceEngine::OutputsDataMap output_info(network.getOutputsInfo()); // 設置輸入格式 for (auto &item : input_info) { auto input_data = item.second; input_data->setPrecision(Precision::U8); input_data->setLayout(Layout::NCHW); input_data->getPreProcess().setResizeAlgorithm(RESIZE_BILINEAR); input_data->getPreProcess().setColorFormat(ColorFormat::BGR); } printf("get it \n"); // 設置輸出格式 for (auto &item : output_info) { auto output_data = item.second; output_data->setPrecision(Precision::FP32); } // 建立可執行網絡對象 auto executable_network = ie.LoadNetwork(network, "CPU"); // 請求推斷圖 auto infer_request = executable_network.CreateInferRequest();
代碼演示步驟中有兩個輸入,對輸入的設置可使用下面的代碼orm
/** Iterating over all input blobs **/ for (auto & item : input_info) { auto input_name = item.first; printf("input_name : %s \n", input_name.c_str()); /** Getting input blob **/ auto input = infer_request.GetBlob(input_name); size_t num_channels = input->getTensorDesc().getDims()[1]; size_t h = input->getTensorDesc().getDims()[2]; size_t w = input->getTensorDesc().getDims()[3]; size_t image_size = h*w; Mat blob_image; resize(src, blob_image, Size(w, h)); printf("input channel : %d, height : %d, width : %d \n", num_channels, h, w); // NCHW unsigned char* data = static_cast<unsigned char*>(input->buffer()); for (size_t row = 0; row < h; row++) { for (size_t col = 0; col < w; col++) { for (size_t ch = 0; ch < num_channels; ch++) { data[image_size*ch + row*w + col] = blob_image.at<Vec3b>(row, col)[ch]; } } } }
最後執行推理,完成對輸出的解析,在解析輸出的時候其實輸的是[NCHW] = [1x3x1080x1920]的浮點數矩陣,須要轉換爲Mat類型爲[HWC] =[1080x1920x3],採用的是循環方式,是否是有更好的數據處理方法能夠轉換這個,值得研究。解析部分的代碼以下xml
// 執行預測 infer_request.Infer(); // 處理輸出結果 for (auto &item : output_info) { auto output_name = item.first; // 獲取輸出數據 auto output = infer_request.GetBlob(output_name); float* buff = static_cast<PrecisionTrait<Precision::FP32>::value_type*>(output->buffer()); const int c = output->getTensorDesc().getDims()[1]; const int h = output->getTensorDesc().getDims()[2]; const int w = output->getTensorDesc().getDims()[3]; // 得到輸出的超像素圖像 Mat result = Mat::zeros(Size(w, h), CV_32FC3); for (int ch = 0; ch < c; ch++) { for (int row = 0; row < h; row++) { for (int col = 0; col < w; col++) { result.at<Vec3f>(row, col)[ch] = buff[ch*w*h+ row*w + col]; } } } printf("channel : %d, height : %d, width : %d \n", c, h, w); normalize(result, result, 0, 255.0, NORM_MINMAX); result.convertTo(result, CV_8U); imshow("High-Resolution Demo", result); imwrite("D:/result.png", result); }
測試結果分別以下:(原圖)
超分辨輸出:(1920x1080)
總結一下:也許模型被簡化的太厲害了,速度是很快了,單身效果感受比雙立方好那麼一點點而已!所謂魚跟熊掌不可兼得!
學習O'pen'VINO請看這裏:
OpenVINO計算機視覺模型加速