T* wiener_filter(const T &src, size_t region_size, double noise_variance) { if ((region_size < 1) || (region_size > std::min(src.nrows(), src.ncols()))) throw std::out_of_range("niblack_threshold: region_size out of range"); // Compute regional statistics. const FloatImageView* means = mean_filter(src, region_size); const FloatImageView* variances = variance_filter(src, *means, region_size); // Compute noise variance if needed. if (noise_variance < 0) { FloatImageData* orderedVariancesData = new FloatImageData(variances->size(), variances->origin()); FloatImageView* orderedVariances = new FloatImageView(*orderedVariancesData); std::copy(variances->vec_begin(), variances->vec_end(), orderedVariances->vec_begin()); size_t area = orderedVariances->nrows() * orderedVariances->ncols(); std::nth_element(orderedVariances->vec_begin(), orderedVariances->vec_begin() + (area - 1) / 2, orderedVariances->vec_end()); noise_variance = (double)*(orderedVariances->vec_begin() + (area - 1) / 2); delete orderedVariancesData; delete orderedVariances; } typedef typename T::value_type value_type; typedef typename ImageFactory<T>::data_type data_type; typedef typename ImageFactory<T>::view_type view_type; data_type* data = new data_type(src.size(), src.origin()); view_type* view = new view_type(*data); for (coord_t y = 0; y < src.nrows(); ++y) { for (coord_t x = 0; x < src.ncols(); ++x) { double mean = (double)means->get(Point(x, y)); double variance = (double)variances->get(Point(x, y)); // The estimate of noise variance will never be perfect, but in // theory, it would be impossible for any region to have a local // variance less than it. The following check eliminates that // theoretical impossibility and has a side benefit of preventing // division by zero. if (variance < noise_variance) { view->set(Point(x, y), (value_type)mean); } else { double multiplier = (variance - noise_variance) / variance; double value = (double)src.get(Point(x, y)); view->set(Point(x, y), (value_type)(mean + multiplier * (value - mean))); } } } delete means->data(); delete means; delete variances->data(); delete variances; return view; }
FloatImageView* variance_filter(const T &src, const FloatImageView &means, size_t region_size) { if ((region_size < 1) || (region_size > std::min(src.nrows(), src.ncols()))) throw std::out_of_range("variance_filter: region_size out of range"); if (src.size() != means.size()) throw std::invalid_argument("variance_filter: sizes must match"); size_t half_region_size = region_size / 2; // Compute squares of each element. This step avoid repeating the squaring // operation for overlapping regions. FloatImageData* squaredData = new FloatImageData(src.size(), src.origin()); FloatImageView* squares = new FloatImageView(*squaredData); transform(src.vec_begin(), src.vec_end(), squares->vec_begin(), double_squared<typename T::value_type>()); FloatImageData* data = new FloatImageData(src.size(), src.origin()); FloatImageView* view = new FloatImageView(*data); for (coord_t y = 0; y < src.nrows(); ++y) { for (coord_t x = 0; x < src.ncols(); ++x) { // Define the region. Point ul((coord_t)std::max(0, (int)x - (int)half_region_size), (coord_t)std::max(0, (int)y - (int)half_region_size)); Point lr((coord_t)std::min(x + half_region_size, src.ncols() - 1), (coord_t)std::min(y + half_region_size, src.nrows() - 1)); squares->rect_set(ul, lr); // Compute the variance. FloatPixel sum = std::accumulate(squares->vec_begin(), squares->vec_end(), (FloatPixel)0); size_t area = squares->nrows() * squares->ncols(); FloatPixel mean = means.get(Point(x,y)); view->set(Point(x, y), sum / area - mean * mean); } } delete squaredData; delete squares; return view; }
FloatPixel image_variance(const T &src) { FloatImageData* squaredData = new FloatImageData(src.size(), src.origin()); FloatImageView* squares = new FloatImageView(*squaredData); transform(src.vec_begin(), src.vec_end(), squares->vec_begin(), double_squared<typename T::value_type>()); FloatPixel sum = std::accumulate(squares->vec_begin(), squares->vec_end(), (FloatPixel)0); size_t area = src.nrows() * src.ncols(); FloatPixel mean = image_mean(src); delete squaredData; delete squares; return sum / area - mean * mean; }