├── image.jpeg ├── readme.md ├── main.cpp └── frst.h /image.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xonxt/frst/HEAD/image.jpeg -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Fast radial symmetry transform using OpenCV 2 | 3 | This is an implementation of the Fast radial symmetry transform using OpenCV. 4 | 5 | _See details_: 6 | Loy, G., & Zelinsky, A. (2002). A fast radial symmetry transform for detecting points of interest. *Computer Vision, ECCV 2002.* 7 | 8 | The code was ported from a MATLAB implementation and tested with OpenCV 3.0.0 (x64). 9 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "frst.h" 2 | 3 | #include 4 | #include 5 | 6 | int main(int argc, char* argv[]) { 7 | 8 | cv::Mat image; 9 | 10 | if (argc > 1) { 11 | image = cv::imread(argv[1]); 12 | } 13 | else { 14 | image = cv::imread("image.jpeg"); 15 | } 16 | 17 | if (!image.data) { 18 | std::cout << "Could not open or find the image" << std::endl; 19 | return -1; 20 | } 21 | 22 | cv::namedWindow("Display window", cv::WINDOW_AUTOSIZE); 23 | 24 | // lose the Alpha channel 25 | if (image.channels() == 4) { 26 | cv::cvtColor(image, image, CV_BGRA2BGR); 27 | } 28 | 29 | // convert to grayscale 30 | cv::Mat grayImg; 31 | cv::cvtColor(image, grayImg, CV_BGR2GRAY); 32 | 33 | // apply FRST 34 | cv::Mat frstImage; 35 | frst2d(grayImg, frstImage, 12, 2, 0.1, FRST_MODE_DARK); 36 | 37 | // the frst will have irregular values, normalize them! 38 | cv::normalize(frstImage, frstImage, 0.0, 1.0, cv::NORM_MINMAX); 39 | frstImage.convertTo(frstImage, CV_8U, 255.0); 40 | 41 | // the frst image is grayscale, let's binarize it 42 | cv::Mat markers; 43 | cv::threshold(frstImage, frstImage, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU); 44 | bwMorph(frstImage, markers, cv::MORPH_CLOSE, cv::MORPH_ELLIPSE, 5); 45 | 46 | // the 'markers' image contains dots of different size. Let's vectorize it 47 | std::vector< std::vector > contours; 48 | std::vector hierarchy; 49 | 50 | contours.clear(); 51 | cv::findContours(markers, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); 52 | 53 | // get the moments 54 | std::vector mu(contours.size()); 55 | for (int i = 0; i < contours.size(); i++) 56 | { 57 | mu[i] = moments(contours[i], false); 58 | } 59 | 60 | // get the mass centers: 61 | std::vector mc(contours.size()); 62 | for (int i = 0; i < contours.size(); i++) 63 | { 64 | mc[i] = cv::Point2f(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00); 65 | } 66 | 67 | // draw the point centers 68 | for (int i = 0; i< contours.size(); i++) 69 | { 70 | cv::circle(image, mc[i], 2, CV_RGB(0,255,0), -1, 8, 0); 71 | } 72 | 73 | // display the image 74 | for (;;) { 75 | cv::imshow("Display window", image); 76 | 77 | char ch = cv::waitKey(10); 78 | 79 | if (char(ch) == 27) 80 | break; 81 | } 82 | 83 | return 0; 84 | } 85 | 86 | -------------------------------------------------------------------------------- /frst.h: -------------------------------------------------------------------------------- 1 | #include "opencv2\core\core.hpp" 2 | #include "opencv2\highgui\highgui.hpp" 3 | #include "opencv2\imgproc\imgproc.hpp" 4 | 5 | #define FRST_MODE_BRIGHT 1 6 | #define FRST_MODE_DARK 2 7 | #define FRST_MODE_BOTH 3 8 | 9 | /** 10 | Calculate vertical gradient for the input image 11 | 12 | @param input Input 8-bit image 13 | @param output Output gradient image 14 | */ 15 | void grady(const cv::Mat& input, cv::Mat &output) 16 | { 17 | output = cv::Mat::zeros(input.size(), CV_64FC1); 18 | for (int y = 0; y(i, j), gy.at(i, j)); 94 | 95 | double gnorm = std::sqrt(g.val[0] * g.val[0] + g.val[1] * g.val[1]); 96 | 97 | if (gnorm > 0) { 98 | 99 | cv::Vec2i gp; 100 | gp.val[0] = (int)std::round((g.val[0] / gnorm) * radii); 101 | gp.val[1] = (int)std::round((g.val[1] / gnorm) * radii); 102 | 103 | if (bright) { 104 | cv::Point ppve(p.x + gp.val[0] + radii, p.y + gp.val[1] + radii); 105 | 106 | O_n.at(ppve.x, ppve.y) = O_n.at(ppve.x, ppve.y) + 1; 107 | M_n.at(ppve.x, ppve.y) = M_n.at(ppve.x, ppve.y) + gnorm; 108 | } 109 | 110 | if (dark) { 111 | cv::Point pnve(p.x - gp.val[0] + radii, p.y - gp.val[1] + radii); 112 | 113 | O_n.at(pnve.x, pnve.y) = O_n.at(pnve.x, pnve.y) - 1; 114 | M_n.at(pnve.x, pnve.y) = M_n.at(pnve.x, pnve.y) - gnorm; 115 | } 116 | } 117 | } 118 | } 119 | 120 | double min, max; 121 | 122 | O_n = cv::abs(O_n); 123 | cv::minMaxLoc(O_n, &min, &max); 124 | O_n = O_n / max; 125 | 126 | M_n = cv::abs(M_n); 127 | cv::minMaxLoc(M_n, &min, &max); 128 | M_n = M_n / max; 129 | 130 | cv::pow(O_n, alpha, S); 131 | S = S.mul(M_n); 132 | 133 | int kSize = std::ceil(radii / 2); 134 | if (kSize % 2 == 0) 135 | kSize++; 136 | 137 | cv::GaussianBlur(S, S, cv::Size(kSize, kSize), radii * stdFactor); 138 | 139 | outputImage = S(cv::Rect(radii, radii, width, height)); 140 | } 141 | 142 | 143 | /** 144 | Perform the specified morphological operation on input image with structure element of specified type and size 145 | @param inputImage Input image of any type (preferrably 8-bit). The resulting image overwrites the input 146 | @param operation Name of the morphological operation (MORPH_ERODE, MORPH_DILATE, MORPH_OPEN, MORPH_CLOSE) 147 | @param mShape Shape of the structure element (MORPH_RECT, MORPH_CROSS, MORPH_ELLIPSE) 148 | @param mSize Size of the structure element 149 | @param iterations Number of iterations, how many times to perform the morphological operation 150 | */ 151 | void bwMorph(cv::Mat& inputImage, const int operation, const int mShape = cv::MORPH_RECT, const int mSize = 3, const int iterations = 1) 152 | { 153 | int _mSize = (mSize % 2) ? mSize : mSize + 1; 154 | 155 | cv::Mat element = cv::getStructuringElement(mShape, cv::Size(_mSize, _mSize)); 156 | cv::morphologyEx(inputImage, inputImage, operation, element, cv::Point(-1, -1), iterations); 157 | } 158 | /** 159 | Perform the specified morphological operation on input image with structure element of specified type and size 160 | @param inputImage Input image of any type (preferrably 8-bit) 161 | @param outputImage Output image of the same size and type as the input image 162 | @param operation Name of the morphological operation (MORPH_ERODE, MORPH_DILATE, MORPH_OPEN, MORPH_CLOSE) 163 | @param mShape Shape of the structure element (MORPH_RECT, MORPH_CROSS, MORPH_ELLIPSE) 164 | @param mSize Size of the structure element 165 | @param iterations Number of iterations, how many times to perform the morphological operation 166 | */ 167 | void bwMorph(const cv::Mat& inputImage, cv::Mat& outputImage, const int operation, const int mShape = cv::MORPH_RECT, const int mSize = 3, const int iterations = 1) 168 | { 169 | inputImage.copyTo(outputImage); 170 | 171 | bwMorph(outputImage, operation, mShape, mSize, iterations); 172 | } --------------------------------------------------------------------------------