Phiên bản giờ là OpenCV 3.1 ngày 2015-12-21, tuy nhiên do chưa đụng tới lâu nên xem thử trước version 2 là OpenCV 2.4.12 ngày 2015-07-30. Download bản dùng cho Windows, code C/C++ xài Visual Studio 2013/Windows nói chung khá là OK. Change logs thì có ở đây. So với những gì còn nhớ thì cách viết chuyển qua C++ hết, để NS, chuyển struct thành class :D. Nói chung ok, có vẻ dễ xài hơn.
|
Setup
Showing Image
#include <opencv/highgui.h>
Dùng với Qt
Chuyển giữa OpenCV::Mat và Qt::QImage Convert between cv::mat and Qimage correctlyxem chi tiết tại Qt and OpenCV.
OpenCV dùng mặc định BGR thay cho RGB. Để hiển thị ảnh grayscale dùng QImage::Format_Grayscale8 hoặc dùng QImage::Format_RGB888 cho ảnh 8UC3.
Đổi colorspace cho pixel
Xác định màu của image, findContours
void initializeRanges() { // Red lower 0°-28° (0-360) colorRanges.push_back(ColorRange(ColorType::Red, cv::Scalar(0, 27, 35), cv::Scalar(14, 255, 255))); // Brown 28°-40° (0-360) colorRanges.push_back(ColorRange(ColorType::Brown, cv::Scalar(14, 27, 35), cv::Scalar(20, 255, 255))); // Orange 40°-46° (0-360) colorRanges.push_back(ColorRange(ColorType::Orange, cv::Scalar(20, 27, 35), cv::Scalar(23, 255, 255))); // Yellow 46°-64° (0-360) colorRanges.push_back(ColorRange(ColorType::Yellow, cv::Scalar(23, 27, 35), cv::Scalar(34, 255, 255))); // Green 64°-146° (0-360) colorRanges.push_back(ColorRange(ColorType::Green, cv::Scalar(34, 27, 35), cv::Scalar(73, 255, 255))); // Aqua 146°-204° (0-360) colorRanges.push_back(ColorRange(ColorType::Aqua, cv::Scalar(73, 27, 35), cv::Scalar(102, 255, 255))); // Aqua-Blue, Blue 204°-254° (0-360) colorRanges.push_back(ColorRange(ColorType::Blue, cv::Scalar(102, 27, 35), cv::Scalar(127, 255, 255))); // Magenta 254°-298° (0-360) colorRanges.push_back(ColorRange(ColorType::Magenta, cv::Scalar(127, 27, 35), cv::Scalar(149, 255, 255))); // Pink 298°-350° (0-360) colorRanges.push_back(ColorRange(ColorType::Pink, cv::Scalar(149, 27, 37), cv::Scalar(175, 255, 255))); // Red upper 350°-360° (0-360) colorRanges.push_back(ColorRange(ColorType::Red, cv::Scalar(175, 27, 35), cv::Scalar(180, 255, 255))); // Black colorRanges.push_back(ColorRange(ColorType::Black, cv::Scalar(0, 0, 0), cv::Scalar(180, 255, 35))); // White (not blue) hue 0°-170° (0-360) & 230°-360° (0-360) colorRanges.push_back(ColorRange(ColorType::White, cv::Scalar(0, 0, 190), cv::Scalar(85, 27, 255))); colorRanges.push_back(ColorRange(ColorType::White, cv::Scalar(115, 0, 190), cv::Scalar(180, 27, 255))); colorRanges.push_back(ColorRange(ColorType::Blue, cv::Scalar(85, 0, 190), cv::Scalar(115, 27, 255))); };
Xác định vùng theo specific color
// Threshold the HSV image to get only specific colors cv::Mat mask, blurred, thresholded; cv::inRange(hsv, lower, upper, mask); cv::GaussianBlur(mask, blurred, cv::Size(9, 9), 5, 5); // Separate out regions of an image corresponding to objects which we want to analyze. // This separation is based on the variation of intensity // between the object pixels and the background pixels. cv::threshold(blurred, thresholded, 120, 255, CV_THRESH_BINARY); // MORPH_RECT vs MORPH_CROSS vs MORPH_ELLIPSE cv::Mat strEl = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(10, 10)); // Morphological opening (remove small objects from the foreground) // Open dilate(erode(src, element)) //cv::erode(thresholded, thresholded, strEl); //cv::dilate(thresholded, thresholded, strEl); cv::morphologyEx(thresholded, thresholded, cv::MORPH_OPEN, strEl); // Morphological closing (fill small holes in the foreground) // Close erode(dilate(src, element)) //cv::dilate(thresholded, thresholded, strEl); //cv::erode(thresholded, thresholded, strEl); cv::morphologyEx(thresholded, thresholded, cv::MORPH_CLOSE, strEl); cv::vector<cv::vector<cv::Point> > contours; cv::vector<cv::Vec4i> hierarchy; // CV_RETR_EXTERNAL/CV_RETR_LIST instead of CV_RETR_TREE // @see http://stackoverflow.com/questions/8830619/difference-between-cv-retr-list-cv-retr-tree-cv-retr-external // Documentation http://opencv.itseez.com/2.4/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?#findcontours // CV_RETR_EXTERNAL gives "outer" contours, so if you have (say) one contour enclosing another // (like concentric circles), only the outermost is given. cv::findContours(thresholded, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE); //cv::RNG rng; //cv::Mat mat = thresholded.clone(); //for (int i = 0; i < contours.size(); i++) { // auto clr = cv::Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)); // cv::drawContours(mat, contours, i, clr, 1, 8, hierarchy, 0, cv::Point()); //}
Color difference
Chuyển qua L*a*b scale và dùng CIE Delta E 1976 algorithm hay CIE Delta E 2000.Code của algorithm này như sau:
double deg2rad(double deg) { return (deg * (M_PI / 180.0)); } double rad2deg(double rad) { return ((180.0 / M_PI) * rad); } // Computes the difference between two BGR colors by converting them to // the L*a*b scale BGR/CIE (CV_BGR2Lab) and comparing them using the // CIE76 algorithm http://en.wikipedia.org/wiki/Color_difference#CIE76 // The 1976 formula is the first color-difference formula that related // a measured to a known set of CIELAB coordinates. This formula has been // succeeded by the 1994 and 2000 formulas because the CIELAB space // turned out to be not as perceptually uniform as intended, especially in // the saturated regions. This means that this formula rates these // colors too highly as opposed to other colors. // Lab, Delta E (standard Euclidean distance) sqrt(L² + a² + b²) // CIE Delta E 1976 // JND: ~2.3 double deltaE1976(const cv::Scalar& lab1, const cv::Scalar& lab2) { // RGB/CIE L*a*b* // 8-bit images: L ← L ∗ 255 / 100, a ← a + 128, b ← b + 128 auto dL = lab1.val[0] - lab2.val[0]; // Covert dL back CIE L*a*b* dL = dL * 100 / 255; auto da = lab1.val[1] - lab2.val[1]; auto db = lab1.val[2] - lab2.val[2]; return cv::sqrt(dL * dL + da * da + db * db); } // http://svn.int64.org/viewvc/int64/colors/colors.js // http://www.ece.rochester.edu/~gsharma/ciede2000/ double deltaE2000(const cv::Scalar& lab1, const cv::Scalar& lab2) { // RGB/CIE L*a*b* // 8-bit images: L ← L ∗ 255 / 100, a ← a + 128, b ← b + 128 auto x = lab1; auto y = lab2; /* * "For these and all other numerical/graphical delta E00 values * reported in this article, we set the parametric weighting factors * to unity(i.e., kL = kC = kH = 1.0)." (Page 27). */ const double kL = 1.0, kC = 1.0, kH = 1.0; const double deg360InRad = deg2rad(360.0); const double deg180InRad = deg2rad(180.0); const double pow25To7 = 6103515625.0; /* pow(25, 7) */ /* * Step 1 */ /* Equation 2 */ double C1 = sqrt((x.val[1] * x.val[1]) + (x.val[2] * x.val[2])); double C2 = sqrt((y.val[1] * y.val[1]) + (y.val[2] * y.val[2])); /* Equation 3 */ double barC = (C1 + C2) / 2.0; /* Equation 4 */ double G = 0.5 * (1 - sqrt(pow(barC, 7) / (pow(barC, 7) + pow25To7))); /* Equation 5 */ double a1Prime = (1.0 + G) * x.val[1]; double a2Prime = (1.0 + G) * y.val[1]; /* Equation 6 */ double CPrime1 = sqrt((a1Prime * a1Prime) + (x.val[2] * x.val[2])); double CPrime2 = sqrt((a2Prime * a2Prime) + (y.val[2] * y.val[2])); /* Equation 7 */ double hPrime1; if (x.val[2] == 0 && a1Prime == 0) { hPrime1 = 0.0; } else { hPrime1 = atan2(x.val[2], a1Prime); /* * This must be converted to a hue angle in degrees between 0 * and 360 by addition of 2 to negative hue angles. */ if (hPrime1 < 0){ hPrime1 += deg360InRad; } } double hPrime2; if (y.val[2] == 0 && a2Prime == 0) { hPrime2 = 0.0; } else { hPrime2 = atan2(y.val[2], a2Prime); /* * This must be converted to a hue angle in degrees between 0 * and 360 by addition of 2 to negative hue angles. */ if (hPrime2 < 0){ hPrime2 += deg360InRad; } } /* * Step 2 */ /* Equation 8 */ double deltaLPrime = y.val[0] - x.val[0]; /* Equation 9 */ double deltaCPrime = CPrime2 - CPrime1; /* Equation 10 */ double deltahPrime; double CPrimeProduct = CPrime1 * CPrime2; if (CPrimeProduct == 0) { deltahPrime = 0; } else { /* Avoid the fabs() call */ deltahPrime = hPrime2 - hPrime1; if (deltahPrime < -deg180InRad) { deltahPrime += deg360InRad; } else if (deltahPrime > deg180InRad) { deltahPrime -= deg360InRad; } } /* Equation 11 */ double deltaHPrime = 2.0 * sqrt(CPrimeProduct) * sin(deltahPrime / 2.0); /* * Step 3 */ /* Equation 12 */ double barLPrime = (x.val[0] + y.val[0]) / 2.0; /* Equation 13 */ double barCPrime = (CPrime1 + CPrime2) / 2.0; /* Equation 14 */ double barhPrime, hPrimeSum = hPrime1 + hPrime2; if (CPrime1 * CPrime2 == 0) { barhPrime = hPrimeSum; } else { if (fabs(hPrime1 - hPrime2) <= deg180InRad) { barhPrime = hPrimeSum / 2.0; } else { if (hPrimeSum < deg360InRad) { barhPrime = (hPrimeSum + deg360InRad) / 2.0; } else { barhPrime = (hPrimeSum - deg360InRad) / 2.0; } } } /* Equation 15 */ double T = 1.0 - (0.17 * cos(barhPrime - deg2rad(30.0))) + (0.24 * cos(2.0 * barhPrime)) + (0.32 * cos((3.0 * barhPrime) + deg2rad(6.0))) - (0.20 * cos((4.0 * barhPrime) - deg2rad(63.0))); /* Equation 16 */ double deltaTheta = deg2rad(30.0) * exp(-pow((barhPrime - deg2rad(275.0)) / deg2rad(25.0), 2.0)); /* Equation 17 */ double RC = 2.0 * sqrt(pow(barCPrime, 7.0) / (pow(barCPrime, 7.0) + pow25To7)); /* Equation 18 */ double SL = 1 + ((0.015 * pow(barLPrime - 50.0, 2.0)) / sqrt(20 + pow(barLPrime - 50.0, 2.0))); /* Equation 19 */ double SC = 1 + (0.045 * barCPrime); /* Equation 20 */ double SH = 1 + (0.015 * barCPrime * T); /* Equation 21 */ double RT = (-sin(2.0 * deltaTheta)) * RC; /* Equation 22 */ double deltaE = sqrt( pow(deltaLPrime / (kL * SL), 2.0) + pow(deltaCPrime / (kC * SC), 2.0) + pow(deltaHPrime / (kH * SH), 2.0) + (RT * (deltaCPrime / (kC * SC)) * (deltaHPrime / (kH * SH)))); return (deltaE); }