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; }
// ----------------------------------------------------------------------------- PyObject* graph_create_minimum_spanning_tree_unique_distances(GraphObject* so, PyObject* images, PyObject* uniq_dists) { PyObject* images_seq = PySequence_Fast(images, "images must be iteratable"); if (images_seq == NULL) return NULL; static PyTypeObject* imagebase = 0; if (imagebase == 0) { PyObject* mod = PyImport_ImportModule(CHAR_PTR_CAST "gamera.gameracore"); if (mod == 0) { PyErr_SetString(PyExc_RuntimeError, "Unable to load gameracore.\n"); Py_DECREF(images_seq); return 0; } PyObject* dict = PyModule_GetDict(mod); if (dict == 0) { PyErr_SetString(PyExc_RuntimeError, "Unable to get module dictionary\n"); Py_DECREF(images_seq); return 0; } imagebase = (PyTypeObject*)PyDict_GetItemString(dict, "Image"); } // get the matrix if (!PyObject_TypeCheck(uniq_dists, imagebase) || get_pixel_type(uniq_dists) != Gamera::FLOAT) { PyErr_SetString(PyExc_TypeError, "uniq_dists must be a float image."); Py_DECREF(images_seq); return 0; } FloatImageView* dists = (FloatImageView*)((RectObject*)uniq_dists)->m_x; if (dists->nrows() != dists->ncols()) { PyErr_SetString(PyExc_TypeError, "image must be symmetric."); Py_DECREF(images_seq); return 0; } // get the graph ready so->_graph->remove_all_edges(); GRAPH_UNSET_FLAG(so->_graph, FLAG_CYCLIC); // make the list for sorting typedef std::vector<std::pair<size_t, size_t> > index_vec_type; index_vec_type indexes(((dists->nrows() * dists->nrows()) - dists->nrows()) / 2); size_t row, col, index = 0; for (row = 0; row < dists->nrows(); ++row) { for (col = row + 1; col < dists->nrows(); ++col) { indexes[index].first = row; indexes[index++].second = col; } } std::sort(indexes.begin(), indexes.end(), DistsSorter(dists)); // Add the nodes to the graph and build our map for later int images_len = PySequence_Fast_GET_SIZE(images_seq); std::vector<Node*> nodes(images_len); int i; for (i = 0; i < images_len; ++i) { GraphDataPyObject* obj = new GraphDataPyObject(PySequence_Fast_GET_ITEM(images_seq, i)); nodes[i] = so->_graph->add_node_ptr(obj); assert(nodes[i] != NULL); } Py_DECREF(images_seq); // create the mst using kruskal i = 0; while (i < int(indexes.size()) && (int(so->_graph->get_nedges()) < (images_len - 1))) { size_t row = indexes[i].first; size_t col = indexes[i].second; cost_t weight = dists->get(Point(col, row)); so->_graph->add_edge(nodes[row], nodes[col], weight); ++i; } RETURN_VOID(); }