Giống tôm thẻ chân chân trắng thương phẩm có màu sắc trong suốt, kích thước từ 10-20mm. Ở phần đầu tôm có chấm màu đen khá lớn so với thân (chiếm 1/3 diện tích). Dựa vào đặc trưng đó để đếm tôm giống tự động.
Các bước thực hiện
– Chuyển ảnh sang ảnh xám
– Lấy ngưỡng nghịch đảo để được ảnh nhị phân
– Dùng hàm distanceTransform() để tìm những blob lớn nhất
– Lấy các blob theo kích cỡ min-max mà người dùng chỉ định
– Xuất kết quả
Hàm distanceTransform() biến đổi ảnh với công thức: pixel nào càng gần background thì giá trị càng nhỏ, pixel nào càng xa background thì giá trị càng lớn.
Kết quả
Các vấn đề lưu ý
– Để tôm vào khay với số lượng vừa phải, ít nước để tôm đừng chồng lấn nhau.
– Để thiết bị chụp ảnh & khay ở vị trí cố định vì phải setup min-max size của tôm.
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
std::vector<std::vector<cv::Point>> ClampContourBySize(std::vector<std::vector<cv::Point>> contours, int minValue, int maxValue) { std::vector<std::vector<cv::Point>> result; for (int i = 0; i < contours.size(); ++i) { cv::Rect r = cv::boundingRect(contours[i]); float distance = abs(r.width - r.height) / (r.width + r.height); if (distance < 0.5) { if (minValue <= r.width &&r.width <= maxValue && minValue <= r.height && r.height <= maxValue) { result.push_back(contours[i]); } } } return result; } int ShrimpCounter(cv::Mat &matInput) { cv::Mat matGray = TGMTimage::ConvertToGray(matInput); cv::imshow("mat gray", matGray); //blur cv::Mat matBlur; cv::blur(matGray, matBlur, cv::Size(29, 29)); //threshold cv::Mat matBinary; cv::adaptiveThreshold(matBlur, matBinary, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, 21, 0); cv::imshow("binary", matBinary); //Finding sure foreground area cv::Mat dist_transform; cv::distanceTransform(matBinary, dist_transform, CV_DIST_L2, 3); cv::Mat matNorm; cv::normalize(dist_transform, matNorm, 0, 1, cv::NORM_MINMAX); cv::imshow("matNorm", matNorm); double min, max; cv::minMaxLoc(dist_transform, &min, &max); cv::Mat sure_fg; cv::threshold(dist_transform, sure_fg, 0.4*max, 255, CV_THRESH_BINARY); cv::imshow("sure_fg", sure_fg); //convert to 1 channel sure_fg.convertTo(sure_fg, CV_8U); std::vector<std::vector<cv::Point>> contours; cv::findContours(sure_fg, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); contours = ClampContourBySize(contours, m_minSize, m_maxSize); if (matInput.channels() == 1) { cv::cvtColor(matInput, matInput, CV_GRAY2BGR); } for (int i = 0; i < contours.size(); i++) { cv::RotatedRect rrect = cv::minAreaRect(contours[i]); cv::rectangle(matInput, rrect.boundingRect, GREEN, 1); } cv::imshow("Output", matInput); return contours.size(); } |