/* * cv_image_deblurring.cpp - iterative blind deblurring * * Copyright (c) 2016-2017 Intel Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Author: Andrey Parfenov <a1994ndrey@gmail.com> * Author: Wind Yuan <feng.yuan@intel.com> */ #include "cv_image_deblurring.h" namespace XCam { CVImageDeblurring::CVImageDeblurring () : CVBaseClass () { _helper = new CVImageProcessHelper (); _sharp = new CVImageSharp (); _edgetaper = new CVEdgetaper (); _wiener = new CVWienerFilter (); } void CVImageDeblurring::set_config (CVIDConfig config) { _config = config; } CVIDConfig CVImageDeblurring::get_config () { return _config; } void CVImageDeblurring::crop_border (cv::Mat &thresholded) { int top = 0; int left = 0; int right = 0; int bottom = 0; for (int i = 0; i < thresholded.rows; i++) { for (int j = 0; j < thresholded.cols; j++) { if (thresholded.at<unsigned char>(i , j) == 255) { top = i; break; } } if (top) break; } for (int i = thresholded.rows - 1; i > 0; i--) { for (int j = 0; j < thresholded.cols; j++) { if (thresholded.at<unsigned char>(i , j) == 255) { bottom = i; break; } } if (bottom) break; } for (int i = 0; i < thresholded.cols; i++) { for (int j = 0; j < thresholded.rows; j++) { if (thresholded.at<unsigned char>(j , i) == 255) { left = i; break; } } if (left) break; } for (int i = thresholded.cols - 1; i > 0; i--) { for (int j = 0; j < thresholded.rows; j++) { if (thresholded.at<unsigned char>(j, i) == 255) { right = i; break; } } if (right) break; } thresholded = thresholded (cv::Rect(left, top, right - left, bottom - top)); } int CVImageDeblurring::estimate_kernel_size (const cv::Mat &image) { int kernel_size = 0; cv::Mat thresholded; cv::Mat dst; cv::Laplacian (image, dst, -1, 3, 1, 0, cv::BORDER_CONSTANT); dst.convertTo (dst, CV_32FC1); cv::filter2D (dst, thresholded, -1, dst, cv::Point(-1, -1), 0, cv::BORDER_CONSTANT); for (int i = 0; i < 10; i++) { cv::Mat thresholded_new; double min_val; double max_val; cv::minMaxLoc (thresholded, &min_val, &max_val); cv::threshold (thresholded, thresholded, round(max_val / 3.5), 255, cv::THRESH_BINARY); thresholded.convertTo (thresholded, CV_8UC1); crop_border (thresholded); if (thresholded.rows < 3) { break; } int filter_size = (int)(std::max(3, ((thresholded.rows + thresholded.cols) / 2) / 10)); if (!(filter_size & 1)) { filter_size++; } cv::Mat filter = cv::Mat::ones (filter_size, filter_size, CV_32FC1) / (float)(filter_size * filter_size - 1); filter.at<float> (filter_size / 2, filter_size / 2) = 0; cv::filter2D (thresholded, thresholded_new, -1, filter, cv::Point(-1, -1), 0, cv::BORDER_CONSTANT); kernel_size = (thresholded_new.rows + thresholded_new.cols) / 2; if (!(kernel_size & 1)) { kernel_size++; } thresholded = thresholded_new.clone(); } return kernel_size; } void CVImageDeblurring::blind_deblurring (const cv::Mat &blurred, cv::Mat &deblurred, cv::Mat &kernel, int kernel_size, float noise_power, bool use_edgetaper) { cv::Mat gray_blurred; cv::cvtColor (blurred, gray_blurred, CV_BGR2GRAY); if (noise_power < 0) { cv::Mat median_blurred; medianBlur (gray_blurred, median_blurred, 3); noise_power = 1.0f / _helper->get_snr (gray_blurred, median_blurred); XCAM_LOG_DEBUG ("estimated inv snr %f", noise_power); } if (kernel_size < 0) { kernel_size = estimate_kernel_size (gray_blurred); XCAM_LOG_DEBUG ("estimated kernel size %d", kernel_size); } if (use_edgetaper) { XCAM_LOG_DEBUG ("edgetaper will be used"); } else { XCAM_LOG_DEBUG ("edgetaper will not be used"); } std::vector<cv::Mat> blurred_rgb (3); cv::split (blurred, blurred_rgb); std::vector<cv::Mat> deblurred_rgb (3); cv::Mat result_deblurred; cv::Mat result_kernel; blind_deblurring_one_channel (gray_blurred, result_kernel, kernel_size, noise_power); for (int i = 0; i < 3; i++) { cv::Mat input; if (use_edgetaper) { _edgetaper->edgetaper (blurred_rgb[i], result_kernel, input); } else { input = blurred_rgb[i].clone (); } _wiener->wiener_filter (input, result_kernel, deblurred_rgb[i], noise_power); _helper->apply_constraints (deblurred_rgb[i], 0); } cv::merge (deblurred_rgb, result_deblurred); result_deblurred.convertTo (result_deblurred, CV_8UC3); fastNlMeansDenoisingColored (result_deblurred, deblurred, 3, 3, 7, 21); kernel = result_kernel.clone (); } void CVImageDeblurring::blind_deblurring_one_channel (const cv::Mat &blurred, cv::Mat &kernel, int kernel_size, float noise_power) { cv::Mat kernel_current = cv::Mat::zeros (kernel_size, kernel_size, CV_32FC1); cv::Mat deblurred_current = _helper->erosion (blurred, 2, 0); float sigmar = 20; for (int i = 0; i < _config.iterations; i++) { cv::Mat sharpened = _sharp->sharp_image_gray (deblurred_current, sigmar); _wiener->wiener_filter (blurred, sharpened.clone (), kernel_current, noise_power); kernel_current = kernel_current (cv::Rect (0, 0, kernel_size, kernel_size)); double min_val; double max_val; cv::minMaxLoc (kernel_current, &min_val, &max_val); _helper->apply_constraints (kernel_current, (float)max_val / 20); _helper->normalize_weights (kernel_current); _wiener->wiener_filter (blurred, kernel_current.clone(), deblurred_current, noise_power); _helper->apply_constraints (deblurred_current, 0); sigmar *= 0.9; } kernel = kernel_current.clone (); } }