Последние статьи
- Твердотельный датчик направления и скорости ветра. Эксперимент
- iPhone на стене в качестве панели управления домом
- MegaD-16M-XT - подсветка выключателей и не только
- Переделка выключателей в кнопки и мини-обзор текущего рынка
- RadSens - модульный счетчик Гейгера с интерфейсом I2C
- "U" - значит универсальный. Обзор модуля MegaD-16U-XT
- SCD4x - современная альтернатива для измерения концентрации CO2
- HTU31D - новый датчик температуры и влажности с нагревательным элементом
- Измерение коэффициента пульсации ламп с помощью MegaD-2561
- Использование солнечных панелей в качестве датчика освещенности
- Согласование датчиков с выходом типа TTL со стандартными входами контроллера
- DPS368 - датчик атмосферного давления индустриального класса повышенной точности
- DS18B20 Waterpoof - импортзамещение
- TMP117 - высокоточный датчик температуры с интерфейсом I2C
- MegaD-16R-XT - расширитель на 16 релейных выходов
- MegaD-2561-RTC V3 - больше портов, зуммер и ИОН
Снятие показаний газового счетчика с помощью Web-камеры
22/03/2011 11:19:58
Интересный и я бы даже сказал оригинальный способ автоматического считывания показаний газового счетчика предлагает постоянный пользователь сайта Tucker.
С целью получения в базу данных Умного Дома фактических показаний газового счетчика пришла идея сделать следующий комплекс. Так как стандартных способов или интерфейсов присоединения к обычному газовому счетчику не существует, было решено снимать показания с помощью фото-видео аппаратуры и распознавать показатели программой. В качестве аппаратуры рассматривались следующие варианты: цифровой фотоаппарат, веб-камера, аналоговая камера. В любом из перечисленных случаев управление устройством должно осуществляться с ПК. Также по понятным причинам необходимо предусмотреть постоянную освещенность счетчика. Как самый простой вариант, для тестирования данной идеи, была выбрана веб-камера со встроенной подсветкой за 320 руб (дешево, сердито и даже сурово). Была куплена вебкамера Defender G-Lens 321, которая к сожалению зависает через пару-тройку часов работы. Возможно дело в подсветке (во всяком случае без нее работает дольше - почти сутки, проблема пока не решена).
Настройка камеры: для оптимального распознавания лучше всего чтобы изображение было черно-белым, разрешение 640х480 (в результате получаем символы размером 20х40) - чем больше тем лучше, контрастность, яркость, sharpness настраиваются оптимально по ситуации. Съемка ведется фото-снимками, раз в минуту, снимки сохраняются в JPEG. Программы для Windows: Timershot (Powertoys XP) - не дает менять разрешение и делать настройки съемки, снимает в цвете; Freelabs Webcam Capture - небольшая программка, не плохой вариант; Willing Webcam Lite - громоздкая, мощная программа, отличные фильтры позволяющие получать четкую картинку, возможность вырезать кусок изображения при сохранении, множество функций типа детектора движения и т.д.
Алгоритм распознавания: для преобразования изображений в числа необходимо решить две задачи:
1) определение местоположения и границ цифр на снимке, которые могут меняться в случае перепозиционирования камеры и изменения показаний, а также удобное сохранение шаблонов из источника (функция обучения)
2) распознавание образов методом сравнения с шаблонами (аналог нейронной сети)
Программа распознает первые 5 цифр (кубометры) и 2 знака после запятой, 3-ий знак, который постоянно крутится - игнорируем. Возникают определенные сложности в случае не точного позиционирования цифр по оси, но эта проблема как-то решена.
Кол-во различающихся между собой шаблонов увеличивает шансы правильного распознавания, но также и замедляет скорость вычислений. Программу надо обучать под конкретные условия использования. В приведенном примере всего 6 шаблонов, что мало и подготовлены они с одного материала, в то время как изображения со счетчика делались разными программами и с разными настройками, что не правильно.
Ниже приводится основная часть скрипта на Perl, готовая к использованию, используются модули GD::Image и Image::Magick, готова для работы в Windows и Unix системах. Тестовые показания и шаблоны прилагаются.
#!/usr/bin/perl # recognizer through templates v2.2 - min / Tucker 110321 # необходимые библиотеки: use GD; use Image::Magick; # используется только в модуле создания шаблонов my $debug=0; # 1 - для режима отладки, выводятся все этапы распознавания, поток направить в файл my $extract=0; # создание шаблонов, начиная с символа 1-7 my $image=$ARGV[0]; # изображение на обработку: JPEG # для упрощения поиска символов, левый верхний угол изображения является началом табло счетчика (черное поле) if ($image!~/.jpg$/i) { print "File format error"; exit; } my $recresult=""; # сюда будет помещен результат распознавания my $myImage = newFromJpeg GD::Image($image); # считываем изображение my $wh_sens=80; # устанавливаем чувствительность белого, R>70 & G>70 & B>70 = White # чем меньше значение, тем больше может быть захвачено мусора # значение должно быть отторировано под конкретные условия освещения # правильнее всего использовать с шаблонами созданными на той же чувствительности my ($img_wdt,$img_hgt)=$myImage->getBounds; # определяем границы изображения, рекомендуется 640+ my $blk_hgt=50/320*$img_wdt; # начальная высота черного блока (50 пикс для разрешения 320) my $blk_top=0; # верхняя граница черного блока, по умолчанию 0 (первая строка) my $let_wdt=0; # ширина искомого, распознаваемого символа my $let_hgt; # высота my $symcount=0; # кол-во распознанных символов my $sx,$sy; # координаты начала искомого символа (левый верхний угол) for $x(0..$img_wdt) { # основной цикл, двигаемся по изображению слева на право X-координата my @col; # содержание текущей колонки for $y($blk_top..$blk_hgt) { # по блоку сверху вниз Y-координата, определяем черный-белый пиксел и запоминаем $index = $myImage->getPixel($x,$y); ($r,$g,$b) = $myImage->rgb($index); $white=0; $val=0; if ( ( $r>$wh_sens )&&( $g>$wh_sens )&&( $b>$wh_sens ) ) { $white=1; $val=1; } # белый! вот чувствительность if (!$x && $white) { $blk_hgt=$y-1; $blk_top=0; last; } # в первый проход по X корректируем высоту блока push(@col,$val); } # конец колонки my $zzz=@col; for ($i=($zzz-1);$i>=($zzz-15);$i--) { # бывает что снизу захватывает белый бордюр, если фото сделано под углом if ($col[$i]) { pop(@col); } # подрезаем мусор else { last; } } my $sum=0; $sum += $_ for @col; # определяем кол-во пикселей в текущей колонке if ($sum) { # если что-то есть, возможно это начало символа $sx, задача определить конец $x if ($let_wdt==0) { $sx=$x; } my $gr=join("",@col); $gr=~s/0/ /g; $gr=~s/1/X/g; print "$x $sum : $gr :$let_wdt" if ($debug); # выводим не пустые столбцы (цифры перевернуты) $let_wdt++; # ширина символа if (($x-$sx)>(13/320*$img_wdt)) { $sum=0; } # если текущая ширина символа превысила нормальную ширину, сбрасываем счетчик } if (!$sum && $let_wdt) { # пустая колонка, видимо конец символа if ($let_wdt<(6/320*$img_wdt)) { goto SKIPTHIS; } # ширина недостаточная для символа, продолжаем поиск # дальше определяем SY-верхнюю координату и EY-нижнюю координату символа my $sy=0,$ey=0; my $blk_mid=$blk_top+int(($blk_hgt-$blk_top)/2); # чтобы откинуть возможный мусор сверху-снизу, двигаемся от середины блока my $midb,$blk_midb; for $blk_midb($blk_mid..$blk_hgt) { # вверх $val=0; for $i($sx..($x-1)) { $index = $myImage->getPixel( $i,$blk_midb ); ( $r,$g,$b ) = $myImage->rgb( $index ); if ( ( $r>$wh_sens )&&( $g>$wh_sens )&&( $b>$wh_sens ) ) { $val++; } } $midb=$blk_midb; if (!$val) { last; } } $ey=$midb; my $midt; for ($blk_midb=($blk_mid-1);$blk_midb>=$blk_top;$blk_midb--) { # и вниз $val=0; for $i($sx..($x-1)) { $index = $myImage->getPixel( $i,$blk_midb ); ( $r,$g,$b ) = $myImage->rgb( $index ); if ( ( $r>$wh_sens )&&( $g>$wh_sens )&&( $b>$wh_sens ) ) { $val++; } # и запоминаем считаем строки с белым } $midt=$blk_midb; if (!$val) { last; } } $sy=$midt+1; #### отладочный print - печатаем найденный символ if ($debug) { print "Symbol detected:"; for $j($sy..($ey-1)) { for $i($sx..($x-1)) { $index = $myImage->getPixel( $i,$j ); ( $r,$g,$b ) = $myImage->rgb( $index ); if ( ( $r>$wh_sens )&&( $g>$wh_sens )&&( $b>$wh_sens ) ) { print "X"; } else { print "_"; } } print ""; } } #### eo print $let_hgt=$ey-$sy; # высота символа if ($let_hgt<(14/320*$img_wdt)) { goto SKIPTHIS; } # высота не достатончая, мусор или запятая, уходим my $prop=$let_wdt/$let_hgt; # определяем пропорцию найденного символа, должно быть ~0,50 print " X: $sx-$x Y: $sy-$ey Cols: $let_wdt Rows: ".$let_hgt." Symbol: $symcount" if $debug; $symcount++; # символ найден, производим опознание путем сравнения с заранее записанными шаблонами # рассчитываем потенциал для каждой цифры, максимальный окажется искомым my @ident_mas = (0,0,0,0,0,0,0,0,0,0); # массив потенциалов для цифр 0..9 my $num_index = 0; for $dig(0..9) { # идем по порядку $#template_pic=-1; # очистим массив, на всякий случай for $ccc(10..20) { # кол-во шаблонов для каждой цифры (должно быть одинаковым) $fnn="./digtemprec/t".$dig."_".$ccc.".png"; if (-e $fnn) { push(@template_pic,$fnn); } } foreach my $template ( @template_pic ) { # перебираем все шаблоны my $tmp_image_bas = newFromPng GD::Image( $template ) || next; # можно читать и newFromJpeg # в случае если шаблон другого размера, надо изменить размеры под распознаваемый символ # а также определить какую часть шаблона задействовать (на случай если искомый символ на счетчике сдвинут - обрезан) my $tmp_image=GD::Image->new($let_wdt,$let_hgt); my ($tx,$ty)=$tmp_image_bas->getBounds; $tsy=0; # позиция смещения в шаблоне, по умолчанию: берем целиком $crp=""; if ($let_hgt<40/640*$img_wdt) { # судя по всему символ обрезан (мало высоты) $tyc=int($ty*(0.5/$prop)); # определяем сколько обрезано, относительно нормы if ($ey>($blk_hgt-16-20)/640*$img_wdt) { # обрезана нижняя или верхняя часть? 16 пикселей от низа блока до низа символов при разрешении 640, еще 20 это половина символа $tsy=0; $ty=$tyc; $crp="*bot $prop"; } else { # top cropped $tsy=$ty-$tyc; $ty=$tyc; $crp="*top $prop"; } } $tmp_image->copyResized($tmp_image_bas,0,0,0,$tsy,$let_wdt,$let_hgt,$tx,$ty); # собственно шаблон готов #$tmp_image->copyResampled #srcf,dstx,y,srcx,y,dstw,h,srcw,h # возможно на больших JPEG почувствуете разницу.... print "$let_hgt/$ey RRR: $tsy,$let_wdt,$let_hgt,$tx,$ty $crp" if ($debug); # сравниваем найденный символ с шаблоном побитно my $crcs=0; for $j($sy..($ey-1)) { my $bitl1="",$bitl2=""; for $i($sx..($x-1)) { $index = $myImage->getPixel( $i,$j ); ( $r1,$g1,$b1 ) = $myImage->rgb( $index ); if ( ( $r1>$wh_sens )&&( $g1>$wh_sens )&&( $b1>$wh_sens ) ) { $bitl1.='1'; } else { $bitl1.='0'; } $pixel = $tmp_image->getPixel($i-$sx, $j-$sy); ($r2, $g2, $b2) = $tmp_image->rgb($pixel); if ( ( $r2>$wh_sens )&&( $g2>$wh_sens )&&( $b2>$wh_sens ) ) { $bitl2.='1'; } else { $bitl2.='0'; } } $bitl1n=oct("0b1".$bitl1); $bitl2n=oct("0b0".$bitl2); $xorr=$bitl1n ^ $bitl2n; # определяем не совпадения построчно и суммируем в $crcs $xorrp=sprintf "%#bn",$xorr; $xorrp=~s/^0b1//;$xorrp=~s/n$//; @sumx=split(//,$xorrp); my $tot=0; ($tot+=$_) for @sumx; $crcs+=$tot; print "$bitl1 $bitl2 : $xorrp $crcs" if $debug; # здесь наглядный вывод: что с чем сравнивается } $crce=1000000/(1+$crcs*$crcs); $ident_mas[$num_index]+=$crce; # расчитываем и сохраняем потенциал print "----$template---($sx..$x)-($sy..$ey)--" if $debug; } # каждый вариант шаблона текущей цифры $num_index++; } # каждая цифра # определяем максимальный потенциал my $max = $ident_mas[0]; my $max_id = 0; for (my $k2 = 1; $k2 < 10; $k2++) { if ($max < $ident_mas[$k2]) { $max = $ident_mas[$k2]; $max_id = $k2; } } print "================ Result symbol N$symcount: ".$max_id."" if $debug; $recresult.=$max_id; # сохраняем найденное значение if ($symcount==5) { $recresult.=","; } #--- утилита создания шаблонов: найденный символ предлагается сохранить в папку шаблонов # чем больше разных шаблонов для каждой цифры тем надежнее и медленнее распознавание if ($extract && $symcount>=$extract) { print "-- Extract: $symcount symbol, is it: $max_id ?"; for $j($sy..($ey-1)) { my $bitl1=""; for $i($sx..($x-1)) { $index = $myImage->getPixel( $i,$j ); ( $r1,$g1,$b1 ) = $myImage->rgb( $index ); if ( ( $r1>$wh_sens )&&( $g1>$wh_sens )&&( $b1>$wh_sens ) ) { $bitl1.='1'; } else { $bitl1.='0'; } } print $bitl1.""; } my $oImageMagick = Image::Magick->new; $oImageMagick->Read($image); $oImageMagick->Crop(x=>$sx, y=>$sy); # вырезаем с SX,SY по X,EY - наш символ $oImageMagick->Crop(width=>$x, height=>$ey); print "Save file? [0-9]: "; $fn=<STDIN>; chomp($fn); if ($fn=~/d/) { my $tmpf="./digtemprec/tmp.png"; $oImageMagick->Write($tmpf); my @filedata=stat($tmpf); my $tmpfs=$filedata[7]; for (10..20) { $fnn="./digtemprec/"."t".$fn."_".$_.".png"; if (-e $fnn) { @filedata=stat($fnn); # [7]=filesize определяем размер файла и сохраняем только разные if ($tmpfs==$filedata[7]) { print "Same size: $fnn ;not saved"; last; } } else { my $winf="$tmpf $fnn"; $winf=~s////g; # это для Windows `copy $winf`; # это для Windows, поменять на cp для Unix print "FILE SAVED: $fnn"; last; } } } } #--- eo extract SKIPTHIS: # сброс счетчиков $let_wdt=0; $sx=0; $sy=0; if ($symcount==7) { last; } } } print "RECOGNITION RESULT: ".$recresult.""; exit;
Полный архив с шаблонами содержит:
- скрипт .pl (для Linux закомментировать строку 213 и поменять в 214 'copy' на 'cp')
- файлы шаблонов (эталонов) для распознавания в директории digtemprec6
- файлы снимков счетчика в качестве примеров в директории recogn6ex
Наверное для обучения и распознавания образов можно было бы использовать библиотеку нейросети FANN, но поскольку я о ней тогда не знал, писал по-своему.
При занесении полученных значений в базу данных, в качестве дополнительного контроля правильности распознавания, имеет смысл убедиться в том, что каждое последующее значение больше предыдущего на величину равную среднему расходу газа за промежуток времени между снятиями показаний, ну и меньше следующего на ту же усредненную величину =)
Точных измерений! =)
Любое использование материалов сайта возможно только с разрешения автора и с обязательным указанием источника.
Добавить комментарий:
Сортировка комментариев: Последние сверху | Первые сверху
2014-01-11 21:40:14 | Andrey_B
Шерхан, на нашем форуме достаточно часто обсуждается применение Raspberry Pi и других мини-ПК в качестве сервера Умного Дома. Думаю, в ряде случаев это вполне оправдано и возможно. А вот Arduino применять в качестве сервера я бы не стал. Очень ограниченные ресурсы, не позволяющие использовать на этой платформе полноценную ОС и высокоуровневые языки программирования. А вот в качестве интеллектуальных исполнителей микроконтроллеры в Умном Доме хороши. Смотрите проект MegaD-328.
2014-01-10 07:34:14 | Шерхан
Вопрос а что если Росбери использовать как комп? или же ардуино с GSM модулем ?
2011-09-02 10:16:02 | Виталий
Мне нужно снимать данные с цифрового дисплея электронных весов (как ЖКИ, так и на светодиодах).
Нужна программа.
За вознаграждение.
alehin_va@mail.ru
2011-06-05 02:15:52 | Владимир
И еще, если эту векамеру програмно выключать и включать, скажем через наждый час ан 5 минут (мне не так уж и сильно нужно показание счетчика совсем в риалтайм), она виснуть не будет?
2011-06-05 02:14:31 | Владимир
А как вы обходите ограничением usb (камера же web с usb) на максимальную длину usb кабеля в 5 метров? (или у вас счетчик находится недалеко от компьютера?)
2011-03-31 10:02:22 | Tucker
Запустил данный модуль под управлением Linux, как и предполагалось, никаких изменений не потребовалось, даже переобучение делать не стал, не смотря на то, что камера под линуксом выдает не совсем качественную мягко-говоря картинку. При том, что камера определилась системой сразу и появился /dev/video0, только в одной программе удалось получить хоть какое-то изображение, это vgrabbj -- как раз то, что нужно для этих целей. В плане зависания камеры -- проблема осталась, но в Linux все намного удобнее в плане диагностики проблем, все очень подробно записывается в логи, к тому же ядро автоматически переподсоединило устройство на /dev/video1, что в windows не возможно.
2011-03-23 10:35:36 | Andrey_B
Открыл тему на форуме
Предлагаю там продолжить обсуждение этой животрепещущей темы. ;)
2011-03-23 08:22:21 | THK
> Интересно, раз там внутри фактически просто геркон - нельзя ли сделать что-то свое?
Обычный геркон не срабатывает, пробовал 4 разных. В выходные попробую "фирменный" магнитный датчик от пневмоцилиндра (в оригинале реагирует на магнитную вставку в поршне, сквозь алюминиевую стенку цылиндра).
Можно еще попробовать датчик холла + операционник, но это фактически изобретение датчика о котором я писал.
> А второй вопрос - как на идущие от счетчика провода все-таки посмотрит контролирующая организация...
Установка датчика предусмотрена конструкцией счетчика, никаких изменений в конструкцию вносится фактически не будет. Я думаю, что их можно смело "посылать".
PS Не пора-ли организовать тему форума типа "Съём показаний счетчиков воды, газа, электричества"? А то обсуждение ушло в сторону от замечательной идеи...
2011-03-23 08:21:38 | Али
Андрей, не скажу за ваши органы, а за наши из Ленобласти могу точно сказать. Когда я поставил этот счетчик я специально вызвал газовщиков. Там если посмотрите на фотки справа от циферблата есть пластиковое ушко. Так вот оно позволяет намертво опечатать датчик к счетчику чтобы не было возможности самому его снять. Газовщики приехали, посмотрели и меня обматерили и сказали, чтобы с такой XXXX я к ним больше не обращался. Как я понял управляющая организация в поселке, доме может самостооятельно прицепить такой датчик без согласования, чтобы отслеживать потребление... Сам датчик идет с бумажкой, где указаны российские сертификаты позволяющие использовать данный девайс.
2011-03-23 00:19:37 | Tucker
Али, спасибо! Не давала покоя мне эта выемка)) Буду искать датчик. А вот как быть со счетчиком электроэнергии ЦЭ2727 ? Может тоже есть секрет?
2011-03-23 00:11:19 | Andrey_B
Вот есть вроде бы в природе датчик импульсов для моего Metrix G10. Называется он Metrix NI-3 (может быть, это тот же IN-Z61?), только вот где его взять - большой вопрос... Интересно, раз там внутри фактически просто геркон - нельзя ли сделать что-то свое? А второй вопрос - как на идущие от счетчика провода все-таки посмотрит контролирующая организация...
2011-03-22 22:03:54 | THK
Поспешил с коментарием... :( Вот, чтобы народ не искал...
Думаю на других счетчиках тоже самое.
Датчик импульсов IN-Z 61 (Z61) для счетчиков газа
Назначение:
Для использования в автоматизированных системах сбора информации счетчик газа может быть оснащен НЧ генератором импульсов IN-Z 6Х, состоящим из 2 язычковых контактов. Один из них срабатывает от магнитной вставки, встроенной в ролик младшего разряда отсчетного устройства. Второй контакт предназначен для сигнализации влияния на контакт внешним магнитным полем.
Описание:
НЧ генератор импульсов выпускается в трех исполнениях:
IN-Z 61 – с разъединяющим кабелем
IN-Z 62 – с зажимами для подключения кабеля
IN-Z 63 – с выходным коннектором
Технические характеристики датчика-генератора импульсов IN-Z 61 (Z61):
- Количество импульсов 100 имп./м3
- Максимальное напряжение 24 В
- Максимальная сила тока 50 мА
- Минимальное количество замыканий 7х107 в мин.
- Максимальная мощность 0,25 Вт
- Минимальное время импульса 0,25 с
- Максимальное сопротивление при замыкании 0,5 Ом
2011-03-22 21:57:14 | THK
У меня BK-G4...
Надо искать датчик.
Али, Вас не затруднит проверить на что реагирует датчик (на металл или на магнит)?
Может и искать ничего не надо?!
2011-03-22 16:57:37 | Andrey_B
Али, главное ведь идея. Ведь аналогичный способ можно применить и в других ситуациях ;)
А вообще вы сказали интересную вещь. У меня вот используется польский газовый счетчик Metrix G10. У него тоже есть под дисплеем выемка. Никогда не задумывался для чего она... Ну, теперь дело чести прицепить его к 1-wire ;) Спасибо, что направили на путь истинный. ;)
2011-03-22 16:40:05 | Али
Воистину слава задору энтузиастов! Особенно когда надо почесать левое ухо правой рукой :) На фотографиях статьи изображен счетчик газа BK-G4 от уважаемой компании Эльстер. Если посмотреть на первую фотку то мы увидим под дисплеем выемку. И она там не просто так!! А она там для датчика импульсов под названием IN-Z61. Выглядит он вот так /www.elstermetering.co.uk/en/2306.html Вещь стандартная, сертифицированная, установка согласований не требует. У производителя в Н.Новгороде стоит порядка 800 рублей. У перекупщиков по городам стоит от 1500 до 2100 руб в зависимости от жадности. Мне доставили от со склада производителя за 1800 руб за 5 дней. Гораздо красивее повесить на этот генератор импульсов 1-wire счетчик стандартный и компьютером его считывать. Если брать счетчик от HobbyBoard, то он с батарейкой. То есть подсчет идет даже когда нет подачи электричества.