Добавлен alexprey,
опубликован
Программирование
Язык:
C#
Добрый вечер.
Недавно откопал у себя несколько ненужных веб-камер и решил побаловать с ними, например написав пару прог. Про одну из них расскажу сегодня, про остальные позже.
Недавно откопал у себя несколько ненужных веб-камер и решил побаловать с ними, например написав пару прог. Про одну из них расскажу сегодня, про остальные позже.
Виртуальный стол
Забавная вещь, вам скажу, если вам нечем заняться, хочется сделать планшет для страдания фигней, то это то что вам нужно. Для этого понадобиться
- веб камера
- карандаш
- изолента
- лист бумаги
- Visual Studio 2010
Как работает
Ниже вы найдете скриншоты каждого этапа алгоритма, а так же немного полезного кода. А тут я расскажу без иллюстраций.
Первый этап
На первом этапе надо получить картинку с нашей веб камеры. Для этого я нашел библиотеку WebCam_Capture, скаченную с какого-то левого сайта, когда-то давно. Работать с ней очень просто, хоть она и корявая, но для отладки таких приложений этого достаточно
Вот собственно инициализация камеры
Вот собственно инициализация камеры
webCam = new WebCamCapture();
webCam.ImageCaptured += new WebCamCapture.WebCamEventHandler(webCam_ImageCaptured);
И обработка полученного изображения
void webCam_ImageCaptured(object source, WebcamEventArgs e)
{
Image img = e.WebCamImage;
vDesctop.ProcessImage(img, ref img);
if (img != null)
{
//Выводим картинку на экран
outputZone.Image = img;
}
}
Второй этап
Как видно из прошлого кусочка кода, я использую такой код для обработки изображения
vDesctop.ProcessImage(img, ref img);
vDesctop - это такой специальный класс, для управления процессом отслеживания нашего, так называемого стилуса
ProcessImage - метод для обработки картинки. Первый параметр - это входное изображение, а второй это указатель на изображение, куда будет записываться результат обработки.
Теперь вернемся к сути, данного этапа. На этом этапе мы вырежем необходимую зону, оставим для обработки, только наш листок, остальное нам будет только мешать. Для этого перебираем каждый пиксель картинки и проверяем на вхождения в четырех угольник, указанный заранее. Спасибо местным умельцам ( Hanabishi) за написание полезных функций на jass, мне оставалось лишь их портировать на C#
ProcessImage - метод для обработки картинки. Первый параметр - это входное изображение, а второй это указатель на изображение, куда будет записываться результат обработки.
Теперь вернемся к сути, данного этапа. На этом этапе мы вырежем необходимую зону, оставим для обработки, только наш листок, остальное нам будет только мешать. Для этого перебираем каждый пиксель картинки и проверяем на вхождения в четырех угольник, указанный заранее. Спасибо местным умельцам ( Hanabishi) за написание полезных функций на jass, мне оставалось лишь их портировать на C#
портированные функции
public static double TriS (double x1, double y1, double x2, double y2, double x3, double y3)
{
return Math.Abs(x1*(y2-y3)+x2*(y3-y1)+x3*(y1-y2))/2;
}
//Принадлежность точки (x;y) треугольнику (x1;y1);(x2;y2);(x3;y3)
public static bool IsCoordsInTriangle(double x, double y, double x1, double y1, double x2, double y2, double x3, double y3)
{
return (int)TriS(x1, y1, x2, y2, x3, y3) == (int)(TriS(x1, y1, x2, y2, x, y)+TriS(x2, y2, x3, y3, x, y)+TriS(x1, y1, x3, y3, x, y));
}
//Принадлежность точки (x;y) произвольному четырёхугольнику (x1;y1);(x2;y2);(x3;y3);(x4;y4). Облегчённая версия, см. примечание.
public static bool IsCoordsIn4GonSimple(double x, double y, double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
{
return IsCoordsInTriangle(x, y, x1, y1, x2, y2, x3, y3) || IsCoordsInTriangle(x, y, x1, y1, x4, y4, x3, y3);
}
//Принадлежность точки (x;y) произвольному четырёхугольнику (x1;y1);(x2;y2);(x3;y3);(x4;y4). Полная версия, см. примечание.
public static bool IsCoordsIn4Gon(double x, double y, double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
{
return IsCoordsInTriangle(x, y, x1, y1, x2, y2, x3, y3) || IsCoordsInTriangle(x, y, x1, y1, x4, y4, x3, y3) || IsCoordsInTriangle(x, y, x1, y1, x2, y2, x4, y4);
}
В итоге, каждый попавший пиксель мы не трогаем, а все остальные закрашиваем другим цветом, чтобы не мешали
Третий этап
На этом этапе мы подсчитываем средний цвет полученного изображения. По скольку большая часть изображения занимает лист бумаги, то благодаря этому мы сможем убрать фон
Четвертый этап
На этом этапе мы находим искомый цвет. Для этого перебираем каждый доступный пиксель и сравниваем с заданным заранее цветом. Если он проходит наш фильтр, то суммируем координаты. Тоесть на этом этапе мы находим средние координаты всех пикселей прошедших наш фильтр.
Пятый этап
Один из самых сложных. На этом этапе наша задача преобразовать координаты из координатной плоскости камеры, в координатную плоскость выделенного прямоугольника. Я долго думал, как это сделать в результате нашел очень оригинальное решение. Код данного преобразования смотрите в приложении. А здесь я лишь расскажу как оно действует.
И так. Что у нас есть. А у нас есть координаты вершин произвольного четырехугольника, задающего границы нашей координатной плоскости. Так же есть координаты точки, которую надо преобразовать. А теперь сплошная математика.
Сперва задаем границы нашего пространства с помощью 4 векторов (top - верхняя граница, bottom - нижняя граница, left - левая граница, right - правая граница). Дальше проводим 2 прямые line1 и line2 (line1 - проведенная из верхнего левого угла в нашу точку и line2 - проведенная из нижнего левого угла в нашу точку). Находим точки пересечения этих прямых с границами top и bottom (line1 пересекаем с bottom, а line2 с top). В результате получаем 2 точки. Дальше получаем вектора отрезков от левых углов, на которой лежит эта точка, до самих точек. Находим заманчивое отношение между длиной данного отрезка и длиной отрезка, на которой точка лежит. Дальше задаем границы нового пространства (В своем случае я нахожу координаты экрана, поэтому задаю экранное пространство) с помощью тех же 4 векторов. Теперь восстанавливаем длину отрезка из отношения, которое мы выяснили чуть ранее. Восстанавливаем координаты точек пересечения линий line1 и line2. Теперь мы можем построить наши линии line1 и line2. Строим их. Находим точку пересечения этих прямых, а эта точка является искомой. Вот и все. Вот такая вот математическая порнография. К сожалению фотографию своих вычислений на листке не могу предоставить, они почему то не хотят опять заливаться.
И так. Что у нас есть. А у нас есть координаты вершин произвольного четырехугольника, задающего границы нашей координатной плоскости. Так же есть координаты точки, которую надо преобразовать. А теперь сплошная математика.
Сперва задаем границы нашего пространства с помощью 4 векторов (top - верхняя граница, bottom - нижняя граница, left - левая граница, right - правая граница). Дальше проводим 2 прямые line1 и line2 (line1 - проведенная из верхнего левого угла в нашу точку и line2 - проведенная из нижнего левого угла в нашу точку). Находим точки пересечения этих прямых с границами top и bottom (line1 пересекаем с bottom, а line2 с top). В результате получаем 2 точки. Дальше получаем вектора отрезков от левых углов, на которой лежит эта точка, до самих точек. Находим заманчивое отношение между длиной данного отрезка и длиной отрезка, на которой точка лежит. Дальше задаем границы нового пространства (В своем случае я нахожу координаты экрана, поэтому задаю экранное пространство) с помощью тех же 4 векторов. Теперь восстанавливаем длину отрезка из отношения, которое мы выяснили чуть ранее. Восстанавливаем координаты точек пересечения линий line1 и line2. Теперь мы можем построить наши линии line1 и line2. Строим их. Находим точку пересечения этих прямых, а эта точка является искомой. Вот и все. Вот такая вот математическая порнография. К сожалению фотографию своих вычислений на листке не могу предоставить, они почему то не хотят опять заливаться.
Приложение
хитрый алгоритм преобразования координат
Vector2D LT = new Vector2D(rect[0].X, rect[0].Y);
Vector2D LB = new Vector2D(rect[1].X, rect[1].Y);
Vector2D RB = new Vector2D(rect[2].X, rect[2].Y);
Vector2D RT = new Vector2D(rect[3].X, rect[3].Y);
Vector2D top = new Vector2D(LT, RT);
Vector2D bottom = new Vector2D(LB, RB);
Vector2D left = new Vector2D(LT, LB);
Vector2D right = new Vector2D(RT, RB);
Vector2D point = new Vector2D(x, y);
Vector2D line1 = point - LT;
Vector2D line2 = point - LB;
Vector2D crossTop = VectorMath.Cross(LB, line2, LT, top);
Vector2D crossBottom = VectorMath.Cross(LT, line1, LB, bottom);
line1 = crossTop - LT;
double l1 = line1.Length() / top.Length();
line1 = crossBottom - LB;
double l2 = line1.Length() / bottom.Length();
LT = new Vector2D(0, 0);
RT = new Vector2D(1366, 0);
LB = new Vector2D(0, 768);
RB = new Vector2D(1366, 768);
top = new Vector2D(LT, RT);
bottom = new Vector2D(LB, RB);
left = new Vector2D(LT, LB);
right = new Vector2D(RT, RB);
line1 = LT + top.GetNormalize() * (l1 * top.Length());
line1 = line1 - LB;
line2 = LB + bottom.GetNormalize() * (l2 * bottom.Length());
line2 = line2 - LT;
point = VectorMath.Cross(LB, line1, LT, line2);
CoordX = (int)Math.Floor(point.x);
CoordY = (int)Math.Floor(point.y);
Чтобы оставить комментарий, пожалуйста, войдите на сайт.
Давно хотел этим заняться, и наверно, займусь когда-нибудь
но если есть какие идеи рассказывай
Отредактирован ScorpioT1000
Отредактирован ScorpioT1000
выглядит это всё как-то так: