ANPR events with picture Hikvision Node

1.8k Views Asked by At

I have subscrived to the events of a Hikvision camera doing a get request to /ISAPI/Event/notification/alertStream. I am interested in ANPR events, this type of events return a multipart response which includes an xml with the event info, a license plate picture and a vehicle picture. All this info came in differents buffers because is a lot of information, for that I convert those buffers in strings and I put them together until I found the separator --boundary--. Now, when I got a complete events I try to handle the event, first I got the xml in which I have the info and I can get correctly all I need, but I am not able to save the image in my computer. Here is an example of the event info:

> Content-Disposition: form-data; name="anpr.xml"; filename="anpr.xml"
> Content-Type: text/xml Content-Length: 2230
> 
> <?xml version="1.0" encoding="UTF-8"?> <EventNotificationAlert
> version="2.0" xmlns="http://www.hikvision.com/ver20/XMLSchema">
> <ipAddress>192.168.55.6</ipAddress>
> <ipv6Address>::ffff:192.168.55.6</ipv6Address> <portNo>80</portNo>
> <protocol>HTTP</protocol> <macAddress>08:a1:89:ff:6e:1a</macAddress>
> <channelID>1</channelID>
> <dateTime>2022-03-16T13:54:03+01:00</dateTime>
> <activePostCount>1</activePostCount> <eventType>ANPR</eventType>
> <eventState>active</eventState>
> <eventDescription>ANPR</eventDescription> <channelName> asdfg
> </channelName> <ANPR> <country>0</country>
> <licensePlate>unknown</licensePlate> <line>1</line>
> <direction>forward</direction> <confidenceLevel>0</confidenceLevel>
> <plateType>unknown</plateType> <plateColor>unknown</plateColor>
> <licenseBright>0</licenseBright> <dangmark>unknown</dangmark>
> <twoWheelVehicle>yes</twoWheelVehicle>
> <plateCharBelieve></plateCharBelieve>
> <vehicleType>nonmotorVehicle</vehicleType> <detectDir>8</detectDir>
> <detectType>5</detectType> <alarmDataType>0</alarmDataType>
> <vehicleInfo> <index>1344</index> <colorDepth>0</colorDepth>
> <color>unknown</color> <speed>0</speed> <length>0</length>
> <vehicleLogoRecog>0</vehicleLogoRecog>
> <vehileSubLogoRecog>0</vehileSubLogoRecog>
> <vehileModel>0</vehileModel> </vehicleInfo> <pictureInfoList>
> <pictureInfo> <fileName>nonMotorPicture.jpg</fileName>
> <type>nonMotorPicture</type> <dataType>0</dataType>
> <picRecogMode>0</picRecogMode> <absTime>20220316135403615</absTime>
> <pId>2022031613540382300NhqQD51oTGTYE</pId> </pictureInfo>
> <pictureInfo> <fileName>detectionPicture.jpg</fileName>
> <type>detectionPicture</type> <dataType>0</dataType>
> <picRecogMode>0</picRecogMode> <absTime>20220316135403615</absTime>
> <plateRect> <X>0</X> <Y>0</Y> <width>0</width> <height>0</height>
> </plateRect> <pId>2022031613540382300KBGZqPyyVYGta</pId>
> </pictureInfo> </pictureInfoList>
> <originalLicensePlate>unknown</originalLicensePlate>
> <CRIndex>0</CRIndex> </ANPR>
> <UUID>2022031613540382300Y3s6LFDQIWOfg73fuU9UVFguFLf40ucwK0ghCc8iVMPG</UUID>
> <picNum>2</picNum> <monitoringSiteID></monitoringSiteID>
> <isDataRetransmission>false</isDataRetransmission>
> </EventNotificationAlert>
> 
> 
> 
> Content-Disposition: form-data;
> name="2022031613540382300NhqQD51oTGTYE.jpg";
> filename="nonMotorPicture.jpg" Content-Type: image/jpeg
> Content-Length: 62407
> 
> ����►JFIF☺☺☺☺���☻☺☺☺☺☺☻☺☺☺☻☻☻☻☻♦♥☻☻☻☻♣♦♦♥♦♠♣♠♠♠♣♠♠♠    ♠        ♠♂
> 
> 
> 
> 
> ♂♀♂ ♀
> 
> 
> ☺☻☻☻☻☻☻♣♥♥♣ ♠ ....
> 
> Content-Disposition: form-data;
> name="2022031613540382300KBGZqPyyVYGta.jpg";
> filename="detectionPicture.jpg" Content-Type: image/jpeg
> Content-Length: 717259
> 
> ����� [ID = 1344 pr= type=0x60] [cur_frm_num=395344
> snap_frm_num=395344 ps_frm_num=0] [obj_rect = 0.023958x0.566667,
> 0.101562x0.292593] [plate_rect = 0.000000x0.000000, 0.000000x0.000000] �D☻�D☻�?�)♦↑⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂X��♥∟☻♣(=→♥♥☻‼☺
> ♥       ☻♦
> ⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂�☺�)♦↑♀♥♠☺�Q�☺�8♦☺♣♥♦☺☻�8�∟z?☻�?�∟z?♦9��>V♫->♦'1h?♦���>�I�>?5�>d;⌂?♦�K↨?333>☻�?�(<?TDD�<◄◄◄?☻�=�Ε>⌂⌂2☺G.☺d☻�☺�♥⌂
> 6778KZ5↓�♥☻6778KZ5↓�♥☻6778KZ5↓�♥☻6778KZ5↓�♥⌂⌂⌂⌂⌂c6778KZ5↓¶#♣⌂⌂⌂⌂⌂j♠⌂▲6778KZ5↓¶#♣⌂⌂⌂⌂⌂j♠⌂▲6778KZ5↓¶#♣⌂⌂⌂⌂⌂j♠⌂▲6778KZ5↓¶#♣⌂⌂⌂⌂⌂j♠⌂▲6778KZ5↓¶#♣⌂⌂⌂⌂⌂♠⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂♠♥♥
> K.☺d☻�☺�♥♦☺�☺C♥7♦☻♣�☺⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂⌂5♂�������♠♦♣♠♣♦♠♠♣♠♠ ►
> 
> 
> ¶♫☼♀►↨¶↑↑↨¶▬▬→↔%▼→∟▬▬↓▼-(0%()(☺
> 
> ‼
> 
> ‼(→▬→((((((((((((((((((((((((((((((((((((((((((((((((((☻
> 
> ‼
> 
> ‼(→▬→((((((((((((((((((((((((((((((((((((((((((((((((((��♦��♥☺"☻◄☺♥◄☻��☺�☺♣☺☺☺☺☺☺☺☻♥♦♣♠
> ♂►☻☺♥♥☻♦♥♣♣♦♦☺}☺☻♥♦◄♣↕!1A♠‼Qa"q¶2��#B��§R��$3br�
> ▬↨↑↓→%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�������������������������������������������������������������������������☺♥☺☺☺☺☺☺☺☺☺☺☻♥♦♣♠
> ♂◄☻☺☻♦♦♥♦♣♦♦☺☻w☺☻♥◄♦♣!1♠↕AQaq‼"2¶B����  #3R�§br�
> ▬$4�%�↨↑↓→&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz��������������������������������������������������������������������������♦x��♀♥☺☻◄♥◄?���N�H�R���r♫☻��☻�j
> ▼�ͱ�♫�◄jijc5♥¶�T�Q榈U!‼�*�x���X��4�0�74��3OZ�→�*�L�)��wݐ§7����⌂�1j;�v��>����A'��☻d���
> ��I�-�ʳ[�4d��E�I↑�#"���8▲���]�‼��_���☻~��صm>�K�7:����P�e♣�☺��\�6Z4�T}�H!z���:����6���;D↨���RW
> 6=W�zU♂]_I�����♀Я�l��B�♣��Y����:♀IzdK���d�M�F&@�p=�z��.�GZgKk�-n��'9�&f☺dC�i>��>�J�\�����▼��.‼��8↓n���sY�~�⌂-���→5�ݔR����→0�N
> �♫F�9�♠j��s↔����♫�lo��O%ͪH�!�z��C☼�▲�z�w↓Ң4i§Φ�%�q↨f��♫G�►v�rz�Yo(��Ķ�]�ڭ�v�eUg��G&��NB6223Y��)d�►�7→����R������↓[��n�OZ׬[WӯⅥ�d◄O♦��o,��'i�u攛�I∟���It.t�#V��qnLA��
> ʌ���♥W��$Վ�$�O��k��hg�\��>����♠��▲՝�☺g�,�↕5��O�D7�♫��n
> ���q�*���a�u;$�/4���.�♥�9E]��T‼��↔;P��3��Ҡ�B��-��↕�§
> ��o�ӣ�c:m���g��-���>�Ժ]�1Gl0Q�uK�►☺�O�m� ⌂��z�o�U�f�#za����pj 
> N�Z�s�p�)���?��n�Xna�u↑I͌��o .....
2

There are 2 best solutions below

0
On

the python code:

from http.server import BaseHTTPRequestHandler, HTTPServer
import xml.etree.ElementTree as ET
import re
import csv
import os

class ANPRRequestHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        # Read the body of the request
        content_length = int(self.headers['Content-Length'])
        post_data = self.rfile.read(content_length)

        # Split the payload into parts
        parts = post_data.split(b'--boundary\r\n')[1:-1]  # Exclude the first and last split elements

        for part in parts:
            headers, body = part.split(b'\r\n\r\n', 1)
            body = body.strip()

            # Process XML part
            if b'anpr.xml' in headers:
                self.process_xml(body)

            # Process image part
            elif b'.jpg' in headers:
                filename = re.findall(rb'filename="([^"]+)"', headers)[0].decode()
                self.save_image(body, filename)

        # Send response
        self.send_response(200)
        self.end_headers()
        self.wfile.write("Received and processed ANPR event".encode('utf-8'))
    def append_to_csv(self, license_plate):
        # CSV file path
        csv_file = 'license_plates.csv'

        # Check if the CSV file exists
        file_exists = os.path.isfile(csv_file)

        # Open the file in append mode
        with open(csv_file, mode='a', newline='') as file:
            writer = csv.writer(file)

            # If the file does not exist, write the header
            if not file_exists:
                writer.writerow(['License Plate'])

            # Write the license plate data
            writer.writerow([license_plate])

    def process_xml(self, xml_data):
        # Parse XML data
        #print(xml_data)
        try:
            
            root = ET.fromstring(xml_data)
            
            # Find the license plate element
            # Update XPath to include namespace prefix
            namespaces = {'ns': 'http://www.hikvision.com/ver20/XMLSchema'}

                # Find the license plate element
            # Update XPath to include namespace prefix
            license_plate_element = root.find('.//ns:ANPR/ns:licensePlate', namespaces)
    
            if license_plate_element is not None and license_plate_element.text:
                license_plate = license_plate_element.text.strip()
                print(f"License Plate: {license_plate}")
                self.append_to_csv(license_plate)
            else:
                print("License plate not found in XML.")
        except ET.ParseError as e:
            print(f"Error parsing XML: {e}")


    def save_image(self, image_data, image_name):
        # Save the image
        with open(image_name, 'wb') as image_file:
            image_file.write(image_data)
        print(f"Saved image {image_name}")

def run(server_class=HTTPServer, handler_class=ANPRRequestHandler, port=8888):
    server_address = ('', port)
    httpd = server_class(server_address, handler_class)
    print(f"Starting httpd on port {port}...")
    httpd.serve_forever()

if __name__ == '__main__':
    run()

and this is the wireshark data:
POST / HTTP/1.1
Content-Type: multipart/form-data; boundary=boundary
Host: 10.199.13.29:8888
Connection: close
Content-Length: 525605

--boundary
Content-Disposition: form-data; name="anpr.xml"; filename="anpr.xml"
Content-Type: text/xml
Content-Length: 2744

<?xml version="1.0" encoding="UTF-8"?>
<EventNotificationAlert version="2.0" xmlns="http://www.hikvision.com/ver20/XMLSchema">
<ipAddress>10.199.27.128</ipAddress>
<portNo>8888</portNo>
<protocol>HTTP</protocol>
<macAddress>d4:e8:53:bc:4c:02</macAddress>
<channelID>1</channelID>
<dateTime>2024-01-19T11:17:19+08:00</dateTime>
<activePostCount>1</activePostCount>
<eventType>ANPR</eventType>
<eventState>active</eventState>
<eventDescription>ANPR</eventDescription>
<channelName>Camera 01</channelName>
<ANPR>
<country>3</country>
<licensePlate>PECT600</licensePlate>
<line>2</line>
<direction>reverse</direction>
<confidenceLevel>97</confidenceLevel>
<plateType>unknown</plateType>
<plateColor>unknown</plateColor>
<licenseBright>0</licenseBright>
<dangmark>no</dangmark>
<twoWheelVehicle>no</twoWheelVehicle>
<threeWheelVehicle>no</threeWheelVehicle>
<plateCharBelieve>99,99,99,99,99,99,99</plateCharBelieve>
<vehicleType>vehicle</vehicleType>
<detectDir>8</detectDir>
<detectType>0</detectType>
<alarmDataType>0</alarmDataType>
<vehicleInfo>
<index>8050</index>
<colorDepth>2</colorDepth>
<color>white</color>
<length>0</length>
<vehicleLogoRecog>1036</vehicleLogoRecog>
<vehileSubLogoRecog>0</vehileSubLogoRecog>
<vehileModel>0</vehileModel>
</vehicleInfo>
<pictureInfoList>
<pictureInfo>
<fileName>licensePlatePicture.jpg</fileName>
<type>licensePlatePicture</type>
<dataType>0</dataType>
<picRecogMode>1</picRecogMode>
<absTime>20240119111719654</absTime>
<pId>2024011911172148100XqTdZBLMwoCkk</pId>
</pictureInfo>
<pictureInfo>
<fileName>detectionPicture.jpg</fileName>
<type>detectionPicture</type>
<dataType>0</dataType>
<picRecogMode>1</picRecogMode>
<absTime>20240119111719654</absTime>
<plateRect>
<X>691</X>
<Y>397</Y>
<width>59</width>
<height>34</height>
</plateRect>
<pId>2024011911172148200AcB8K3CS9B2h5</pId>
</pictureInfo>
</pictureInfoList>
<originalLicensePlate>PECT600</originalLicensePlate>
<CRIndex>3</CRIndex>
<vehicleListName>otherList</vehicleListName>
<plateSize>0</plateSize>
</ANPR>
<UUID>2024011911172144900FlERIduqg06eKsYuvLX072NugIxaHw22mHG5D5KBmWqX</UUID>
<picNum>2</picNum>
<monitoringSiteID></monitoringSiteID>
<isDataRetransmission>false</isDataRetransmission>
<DeviceGPSInfo>
<longitudeType>E</longitudeType>
<latitudeType>N</latitudeType>
<Longitude>
<degree>111</degree>
<minute>0</minute>
<sec>0.000000</sec>
</Longitude>
<Latitude>
<degree>0</degree>
<minute>22</minute>
<sec>0.000000</sec>
</Latitude>
</DeviceGPSInfo>
<detectionBackgroundImageResolution>
<height>1568</height>
<width>2560</width>
</detectionBackgroundImageResolution>
<countryAbbreviation>NON</countryAbbreviation>
</EventNotificationAlert>

--boundary
Content-Disposition: form-data; name="licensePlatePicture.jpg"; filename="2024011911172148100XqTdZBLMwoCkk.jpg"
Content-Type: image/jpeg
Content-Length: 5940

......JFIF..
1
On

Man its a long way ahead. Bear with me.

  1. As you get the xml I suppose you have correctly configured the ANPR listening port in the camera. Now its
  2. Install PHP 8.0 (No web server need as of now to test)
  3. By default the Camera should post in the <<Listening_Server>>:<<Listening_Port>>/test
  4. Create a folder named php_anpr and inside it create a folder test
  5. Inside test folder create another folder images
  6. create a file in test folder named index.php and paste the following code
<?php
//php -S SERVER:PORT -t test

$id = '_'. date('Ymd_hmis');


//file_put_contents('images/LPR_JSON'.$id.'.json', json_encode($_FILES));

function WriteSingleFile($xid) {
    
    foreach ($_FILES as $file) {
        $key = $file['name'];
        $info = pathinfo($file['name']);
        $tempfilename = $file['tmp_name'];
        $extension = $info['extension'];
        $newname = 'LPR'.$xid.'_'.$key;
        $target = 'images/'.$newname;
        move_uploaded_file( $tempfilename, $target);
    }
}

WriteSingleFile($id);

?>
  1. Now open the php_anpr folder in windows terminal and paste the following after replacing the IP of your pc and port (8080) - Note: you must also configure camera to send anpr information in the same IP and port
php -S SERVER:PORT -t test

Wait for some vehicle to be triggered in the camera alarmstream

It will send you at least 2 files detectionpicture and anpr.xml Only if licenseplate is detected, you will get License plate picture.jpg