Нарды – игра, где многое зависит не только от стратегии игрока, но и от везения с кубиками. Которое, в свою очередь, зависит от личности бросающего кости, от его актуального состояния, от положения звезд, и бог знает чего еще. Во всяком случае, статистику смотреть интересно. Но, набивать ее вручную лень, да и отвлекает от игры. Поэтому сразу возникла мысль этот процесс автоматизировать - «лучше день потерять, а потом за полчаса долететь». Тем более в будущем может пригодиться (см первое предложение).
В архиве - результат моих стараний в виде проекта под NET и C# (для тех, у кого нет на них аллергии). Не желающие разбираться в коде могут сразу попробовать запустить экзэшник из папки Dice Detection OpenCV\bin\Release. Не исключено, что заработает, но не факт. Так что пользоваться не призываю. Приложение делал для себя, и про универсальность не особо задумывался. Если все же кто-то захочет попробовать, и возникнут трудности – будем разбираться. По крайней мере, на пробном «чистом» ноутбуке запустилось без проблем.
Приложение выполняет 2 задачи: собственно распознавание значений кубиков, и занесение результатов игры на лист Excel в файл "Nardy.xlsx" (должен находится там же, где Dice Detection OpenCV.exe, название менять нельзя). Файл можно открыть заранее, либо приложение попытается это сделать за вас. Распознавалка будет работать и без статистики, подсчитывая только количество выброшенных дублей, но это не так интересно. Справка по интерфейсу в файле Help.pdf в той же папке.
TTS зацепится за первый попавшийся в системе русскоязычный движок. Если ни один голос не установлен, то будет распознавать кости молча.
Теперь о самой магии, предоставляемой библиотекой EMGU.CV:
- Код: Выделить всё • Развернуть
Emgu.CV.Cvb.CvBlobs resultingImgBlobs = new Emgu.CV.Cvb.CvBlobs();
Emgu.CV.Cvb.CvBlobDetector bDetect = new Emgu.CV.Cvb.CvBlobDetector();
У меня белые кубики с черными точками на черном фоне. Поэтому, для начала, делаем ч.б. изображение, инвертируем и выравниваем цвета:
- Код: Выделить всё • Развернуть
Image <Gray, Byte> greyThreshImg = cvImage.ThresholdBinaryInv(new Gray(GrayIndex), new Gray(255));
cvImage, то, что получили с камеры. Пороговое значение GrayIndex около 200, подбирается в зависимости от освещенности.
Затем, собственно, запускаем поиск «блобсов»:
- Код: Выделить всё • Развернуть
uint numWebcamBlobsFound = bDetect.Detect(greyThreshImg, resultingImgBlobs);
Такой умный код я, конечно, подсмотрел в интернете. Остальное придумал сам.
Дальше осталось отобрать нужные по размеру кружочки и рассортировать их по кубикам, анализируя расстояния между точками:
- Код: Выделить всё • Развернуть
foreach (Emgu.CV.Cvb.CvBlob targetBlob in resultingImgBlobs.Values)
{
int CentroidX = (int)targetBlob.Centroid.X;
int CentroidY = (int)targetBlob.Centroid.Y;
int BlobArea = targetBlob.Area;
if (BlobArea > MinBlobArea && BlobArea < 1000){...}
...
}
Такой подход довольно простой, и имеет свои недостатки.
Во-первых: желательно ограничить пространство для кубиков, или поднять камеру повыше, чтобы на изображение не очень попадали боковые грани.
Во- вторых, когда кубики плотно прикасаются углами, может получиться, что метод расстояний ошибается. Но в 90% случаев определяет правильно, а для 10 оставшихся предусмотрена быстрая ручная корректировка статистики последнего хода.
В любом случае, надо произвести настройки и калибровку под свой кубик (См. Help)
Я использовал в качестве камеры мобильник с приложением DroidCam, только потому, что у меня не было под рукой подходящей вебки. У меня получилось, что поддончик для кубиков целиком занимает весь кадр. В зависимости от камеры и подставки, может оказаться, что надо будет вырезать только часть кадра для анализа. Средствами EMGU.CV это сделать не сложно. Либо просто не рассматривать «блобсы», координаты которых выходят за определенные рамки. Это еще проще.
Пора задуматься, как, собственно, робот будет кидать кости. Пока на ум приходит специальный стакан….