#include
#include
#include
#include
using namespace cv;
using namespace std;
//é对ç°åº¦å¾çä¸å¼æ»¤æ³¢+CVPR 2019çSideWindowFilter
//å
¶ä»ç§ç±»çæ»¤æ³¢ç´æ¥æ¢æ ¸å³å¯
//è®°å½æ¯ä¸ä¸ªæ¹åçæ ¸çä¸ä¸º0çå
ç´ ä¸ªæ°
int cnt[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
// è®°å½æ¯ä¸ä¸ªæ¹åçæ»¤æ³¢å¨
vector filter[8];
//åå§ååå¾ä¸ºradiusçæ»¤æ³¢å¨ï¼åçå¯ä»¥çhttps://mp.weixin.qq.com/s/vjzZjRoQw7MnkqAfvwBUNA
void InitFilter(int radius) {
int n = radius * 2 + 1;
for (int i = 0; i < 8; i++) {
cnt[i] = 0;
filter[i].clear();
}
for (int i = 0; i < 8; i++) {
for (int x = 0; x < n; x++) {
for (int y = 0; y < n; y++) {
if (i == 0 && x <= radius && y <= radius) {
filter[i].push_back(1);
}
else if (i == 1 && x <= radius && y >= radius) {
filter[i].push_back(1);
}
else if (i == 2 && x >= radius && y <= radius) {
filter[i].push_back(1);
}
else if (i == 3 && x >= radius && y >= radius) {
filter[i].push_back(1);
}
else if (i == 4 && x <= radius) {
filter[i].push_back(1);
}
else if (i == 5 && x >= radius) {
filter[i].push_back(1);
}
else if (i == 6 && y >= radius) {
filter[i].push_back(1);
}
else if (i == 7 && y <= radius) {
filter[i].push_back(1);
}
else {
filter[i].push_back(0);
}
}
}
}
for (int i = 0; i < 8; i++) {
int sum = 0;
for (int j = 0; j < filter[i].size(); j++) sum += filter[i][j] == 1;
cnt[i] = sum;
}
}
//å®ç°Side Window Filterçä¸å¼æ»¤æ³¢ï¼å¼ºå¶ä¿è¾¹
Mat MedianSideWindowFilter(Mat src, int radius = 1) {
int row = src.rows;
int col = src.cols;
int channels = src.channels();
InitFilter(radius);
//é对ç°åº¦å¾
vector now;
if (channels == 1) {
Mat dst(row, col, CV_8UC1);
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (i < radius || i + radius >= row || j < radius || j + radius >= col) {
dst.at(i, j) = src.at(i, j);
continue;
}
int minn = 256;
int pos = 0;
for (int k = 0; k < 8; k++) {
int val = 0;
int id = 0;
now.clear();
for (int x = -radius; x <= radius; x++) {
for (int y = -radius; y <= radius; y++) {
//if (x == 0 && y == 0) continue;
if (filter[k][id]) now.push_back(src.at(i + x, j + y) * filter[k][id]);
id++;
//val += src.at(i + x, j + y) * filter[k][id++];
}
}
sort(now.begin(), now.end());
int mid = (int)(now.size());
val = now[mid / 2];
if (abs(val - src.at(i, j)) < minn) {
minn = abs(val - src.at(i, j));
pos = k;
}
}
int val = 0;
int id = 0;
now.clear();
for (int x = -radius; x <= radius; x++) {
for (int y = -radius; y <= radius; y++) {
//if (x == 0 && y == 0) continue;
if (filter[pos][id]) now.push_back(src.at(i + x, j + y) * filter[pos][id]);
id++;
//val += src.at(i + x, j + y) * filter[k][id++];
}
}
sort(now.begin(), now.end());
int mid = (int)(now.size());
val = now[mid / 2];
dst.at(i, j) = val;
}
}
return dst;
}
//é对RGBå¾
Mat dst(row, col, CV_8UC3);
for (int c = 0; c < 3; c++) {
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (i < radius || i + radius >= row || j < radius || j + radius >= col) {
dst.at(i, j)[c] = src.at(i, j)[c];
continue;
}
int minn = 256;
int pos = 0;
for (int k = 0; k < 8; k++) {
int val = 0;
int id = 0;
now.clear();
for (int x = -radius; x <= radius; x++) {
for (int y = -radius; y <= radius; y++) {
//if (x == 0 && y == 0) continue;
//val += src.at(i + x, j + y)[c] * filter[k][id++];
if (filter[k][id]) now.push_back(src.at(i + x, j + y)[c] * filter[k][id]);
id++;
}
}
sort(now.begin(), now.end());
int mid = (int)(now.size());
val = now[mid / 2];
if (abs(val - src.at(i, j)[c]) < minn) {
minn = abs(val - src.at(i, j)[c]);
pos = k;
}
}
int val = 0;
int id = 0;
now.clear();
for (int x = -radius; x <= radius; x++) {
for (int y = -radius; y <= radius; y++) {
//if (x == 0 && y == 0) continue;
//val += src.at(i + x, j + y)[c] * filter[k][id++];
if (filter[pos][id]) now.push_back(src.at(i + x, j + y)[c] * filter[pos][id]);
id++;
}
}
sort(now.begin(), now.end());
int mid = (int)(now.size());
val = now[mid / 2];
dst.at(i, j)[c] = val;
}
}
}
return dst;
}
const double eps = 1e-7;
//è·åpt0->pt1åéåpt0->pt2åéä¹é´ç夹è§
static double angle(Point pt1, Point pt2, Point pt0)
{
double dx1 = pt1.x - pt0.x;
double dy1 = pt1.y - pt0.y;
double dx2 = pt2.x - pt0.x;
double dy2 = pt2.y - pt0.y;
return (dx1*dx2 + dy1*dy2) / sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + eps);
}
//寻æ¾ç©å½¢
static void findSquares(const Mat& image, vector >& squares, int N = 5, int thresh = 50)
{
//滤波å¯ä»¥æåè¾¹ç¼æ£æµçæ§è½
Mat timg(image);
// æ®éä¸å¼æ»¤æ³¢
//medianBlur(image, timg, 9);
// SideWindowFilterçä¸å¼æ»¤æ³¢
timg = MedianSideWindowFilter(image, 4);
Mat gray0(timg.size(), CV_8U), gray;
// åå¨è½®å»
vector > contours;
// å¨å¾åçæ¯ä¸ä¸ªé¢è²éé寻æ¾ç©å½¢
for (int c = 0; c < 3; c++)
{
int ch[] = { c, 0 };
// 彿°åè½ï¼mixChannels主è¦å°±æ¯æè¾å
¥çç©éµï¼æç©éµæ°ç»ï¼çæäºééæåå¤å¶ç»å¯¹åºçè¾åºç©éµï¼æç©éµæ°ç»ï¼çæäºééä¸ï¼å
¶ä¸ç对åºå
³ç³»å°±ç±fromToåæ°å¶å®.
// æ¥å£ï¼void mixChannels (const Mat* src , int nsrc , Mat* dst , int ndst , const int* fromTo , size_t npairs );
// src: è¾å
¥ç©éµï¼å¯ä»¥ä¸ºä¸ä¸ªä¹å¯ä»¥ä¸ºå¤ä¸ªï¼ä½æ¯ç©éµå¿
é¡»æç¸åç大å°å深度.
// nsrc: è¾å
¥ç©éµç个æ°.
// dst: è¾åºç©éµï¼å¯ä»¥ä¸ºä¸ä¸ªä¹å¯ä»¥ä¸ºå¤ä¸ªï¼ä½æ¯ææçç©éµå¿
é¡»äºå
åé
空é´ï¼å¦ç¨createï¼ï¼å¤§å°å深度须ä¸è¾å
¥ç©éµçå.
// ndst: è¾åºç©éµç个æ°
// fromTo:设置è¾å
¥ç©éµçéé对åºè¾åºç©éµçééï¼è§åå¦ä¸ï¼é¦å
ç¨æ°åæ è®°è¾å
¥ç©éµçå个ééãè¾å
¥ç©éµä¸ªæ°å¯è½å¤äºä¸ä¸ªå¹¶ä¸æ¯ä¸ªç©éµçééå¯è½ä¸ä¸æ ·ï¼
// 第ä¸ä¸ªè¾å
¥ç©éµçééæ è®°èå´ä¸ºï¼0 ~src[0].channels() - 1ï¼ç¬¬äºä¸ªè¾å
¥ç©éµçééæ è®°èå´ä¸ºï¼src[0].channels() ~src[0].channels() + src[1].channels() - 1,
// 以æ¤ç±»æ¨ï¼å
¶æ¬¡è¾åºç©éµä¹ç¨åæ ·çè§åæ è®°ï¼ç¬¬ä¸ä¸ªè¾åºç©éµçééæ è®°èå´ä¸ºï¼0 ~dst[0].channels() - 1ï¼ç¬¬äºä¸ªè¾å
¥ç©éµçééæ è®°èå´ä¸ºï¼dst[0].channels()
// ~dst[0].channels() + dst[1].channels() - 1, 以æ¤ç±»æ¨ï¼æåï¼æ°ç»fromToç第ä¸ä¸ªå
ç´ å³fromTo[0]åºè¯¥å¡«å
¥è¾å
¥ç©éµçæä¸ªééæ è®°ï¼èfromToç第äºä¸ªå
ç´ å³
// fromTo[1]åºè¯¥å¡«å
¥è¾åºç©éµçæä¸ªééæ è®°ï¼è¿æ ·å½æ°å°±ä¼æè¾å
¥ç©éµçfromTo[0]éééé¢çæ°æ®å¤å¶ç»è¾åºç©éµçfromTo[1]ééãfromToåé¢çå
ç´ ä¹æ¯è¿ä¸ª
// éçï¼æ»ä¹å°±æ¯ä¸ä¸ªè¾å
¥ç©éµçééæ è®°åé¢å¿
é¡»è·ç个è¾åºç©éµçééæ è®°.
// npairs: å³åæ°fromToä¸çæå ç»è¾å
¥è¾åºééå
³ç³»ï¼å
¶å®å°±æ¯åæ°fromToçæ°ç»å
ç´ ä¸ªæ°é¤ä»¥2.
mixChannels(&timg, 1, &gray0, 1, ch, 1);
// å°è¯å 个ä¸åçéå¼
for (int l = 0; l < N; l++)
{
// hack: use Canny instead of zero threshold level.
// Canny helps to catch squares with gradient shading
// å¨çº§å«ä¸º0çæ¶åä¸ä½¿ç¨éå¼ä¸º0ï¼èæ¯ä½¿ç¨Cannyè¾¹ç¼æ£æµç®å
if (l == 0)
{
// void Canny( InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize = 3, bool L2gradient = false);
// 第ä¸ä¸ªåæ°ï¼è¾å
¥å¾åï¼å
«ä½çå¾åï¼
// 第äºä¸ªåæ°ï¼è¾åºçè¾¹ç¼å¾å
// 第ä¸ä¸ªåæ°ï¼ä¸ééå¼ï¼å¦æåç´ æ¢¯åº¦ä½äºä¸ééå¼ï¼åå°åç´ ä¸è¢«è®¤ä¸ºè¾¹ç¼
// 第åä¸ªåæ°ï¼ä¸ééå¼ï¼å¦æåç´ æ¢¯åº¦é«äºä¸ééå¼ï¼åå°åç´ è¢«è®¤ä¸ºæ¯è¾¹ç¼ï¼å»ºè®®ä¸éæ¯ä¸éç2åæè
3åï¼
// 第äºä¸ªåæ°ï¼ä¸ºSobel()è¿ç®æä¾å
æ ¸å¤§å°ï¼é»è®¤å¼ä¸º3
// 第å
ä¸ªåæ°ï¼è®¡ç®å¾å梯度å¹
å¼çæ å¿ï¼é»è®¤å¼ä¸ºfalse
Canny(gray0, gray, 5, thresh, 5);
// æ§è¡å½¢æå¦è¨èæä½
dilate(gray, gray, Mat(), Point(-1, -1));
}
else
{
// å½lä¸çäº0çæ¶åï¼æ§è¡ tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
gray = gray0 >= (l + 1) * 255 / N;
}
// 寻æ¾è½®å»å¹¶å°å®ä»¬å
¨é¨åå¨ä¸ºå表
findContours(gray, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
//åå¨ä¸ä¸ªå¤è¾¹å½¢ï¼ç©å½¢ï¼
vector approx;
// æµè¯æ¯ä¸ä¸ªè½®å»
for (size_t i = 0; i < contours.size(); i++)
{
// è¿ä¼¼è½®å»ï¼ç²¾åº¦ä¸è½®å»å¨é¿ææ£æ¯,主è¦åè½æ¯æä¸ä¸ªè¿ç»å
æ»æ²çº¿æçº¿åï¼å¯¹å¾åè½®å»ç¹è¿è¡å¤è¾¹å½¢æåã
// 彿°å£°æï¼void approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed)
// InputArray curve:ä¸è¬æ¯ç±å¾åçè½®å»ç¹ç»æçç¹é
// OutputArray approxCurveï¼è¡¨ç¤ºè¾åºçå¤è¾¹å½¢ç¹é
// double epsilonï¼ä¸»è¦è¡¨ç¤ºè¾åºç精度ï¼å°±æ¯ä¸¤ä¸ªè½®å»ç¹ä¹é´æå¤§è·ç¦»æ°ï¼5,6,7ï¼ï¼8ï¼ï¼,,ï¼
// bool closedï¼è¡¨ç¤ºè¾åºçå¤è¾¹å½¢æ¯å¦å°é
// arcLength 计ç®å¾åè½®å»çå¨é¿
approxPolyDP(Mat(contours[i]), approx, 20, true);
// è¿ä¼¼åï¼æ¹å½¢è½®å»åºå
·æ4个顶ç¹
// ç¸å¯¹è¾å¤§çåºåï¼ä»¥æ»¤é¤åæçè½®å»ï¼å¹¶ä¸æ¯å¸éã
// 注æ: 使ç¨é¢ç§¯çç»å¯¹å¼ï¼å 为é¢ç§¯å¯ä»¥æ¯æ£å¼æè´å¼-æ ¹æ®è½®å»æ¹å
if (approx.size() == 4 &&
fabs(contourArea(Mat(approx))) > 1000 &&
isContourConvex(Mat(approx)))
{
double maxCosine = 0;
for (int j = 2; j < 5; j++)
{
// æ¾å°ç¸é»è¾¹ä¹é´çè§åº¦çæå¤§ä½å¼¦
double cosine = fabs(angle(approx[j % 4], approx[j - 2], approx[j - 1]));
maxCosine = MAX(maxCosine, cosine);
}
// 妿ææè§åº¦çä½å¼¦é½å¾å°(ææè§åº¦å为90度)ï¼å°é¡¶ç¹éååå
¥ç»ævector
if (maxCosine < 0.3)
squares.push_back(approx);
}
}
}
}
}
//å¨å¾åä¸ç»åºæ¹å½¢
void drawSquares(Mat &image, const vector >& squares) {
for (size_t i = 0; i < squares.size(); i++)
{
const Point* p = &squares[i][0];
int n = (int)squares[i].size();
//䏿£æµè¾¹ç
if (p->x > 3 && p->y > 3)
polylines(image, &p, &n, 1, true, Scalar(0, 255, 0), 3, LINE_AA);
}
}
int main() {
Mat src = cv::imread("F:\\stone.jpg");
vector > squares;
findSquares(src, squares, 5, 50);
drawSquares(src, squares);
imshow("result", src);
imwrite("F:\\res2.jpg", src);
waitKey(0);
return 0;
}