summaryrefslogtreecommitdiff
path: root/python/openvino/runtime/streaming
diff options
context:
space:
mode:
authorEric Dao <eric@erickhangdao.com>2025-03-10 17:54:31 -0400
committerEric Dao <eric@erickhangdao.com>2025-03-10 17:54:31 -0400
commitab224e2e6ba65f5a369ec392f99cd8845ad06c98 (patch)
treea1e757e9341863ed52b8ad4c5a1c45933aab9da4 /python/openvino/runtime/streaming
parent40da1752f2c8639186b72f6838aa415e854d0b1d (diff)
downloadthesis-master.tar.gz
thesis-master.tar.bz2
thesis-master.zip
completed thesisHEADmaster
Diffstat (limited to 'python/openvino/runtime/streaming')
-rw-r--r--python/openvino/runtime/streaming/ed0_streaming_example/README.md14
-rw-r--r--python/openvino/runtime/streaming/ed0_streaming_example/system_console_script.tcl365
-rw-r--r--python/openvino/runtime/streaming/ed0_streaming_example/system_console_script_perf.tcl190
-rw-r--r--python/openvino/runtime/streaming/image_streaming_app/CMakeLists.txt36
-rw-r--r--python/openvino/runtime/streaming/image_streaming_app/bmp_file.cpp277
-rw-r--r--python/openvino/runtime/streaming/image_streaming_app/bmp_file.h47
-rw-r--r--python/openvino/runtime/streaming/image_streaming_app/command_line.cpp72
-rw-r--r--python/openvino/runtime/streaming/image_streaming_app/command_line.h31
-rw-r--r--python/openvino/runtime/streaming/image_streaming_app/float16.h204
-rw-r--r--python/openvino/runtime/streaming/image_streaming_app/image_streaming_app.cpp306
-rw-r--r--python/openvino/runtime/streaming/image_streaming_app/image_streaming_app.h79
-rw-r--r--python/openvino/runtime/streaming/image_streaming_app/layout_transform/CMakeLists.txt35
-rw-r--r--python/openvino/runtime/streaming/image_streaming_app/layout_transform/include/ILayoutTransform.h38
-rw-r--r--python/openvino/runtime/streaming/image_streaming_app/layout_transform/source/LayoutTransform.cpp51
-rw-r--r--python/openvino/runtime/streaming/image_streaming_app/layout_transform/source/LayoutTransform.h38
-rw-r--r--python/openvino/runtime/streaming/image_streaming_app/raw_image.cpp225
-rw-r--r--python/openvino/runtime/streaming/image_streaming_app/raw_image.h52
-rw-r--r--python/openvino/runtime/streaming/image_streaming_app/uio/CMakeLists.txt35
-rw-r--r--python/openvino/runtime/streaming/image_streaming_app/uio/include/IUioDevice.h40
-rw-r--r--python/openvino/runtime/streaming/image_streaming_app/uio/source/UioDevice.cpp168
-rw-r--r--python/openvino/runtime/streaming/image_streaming_app/uio/source/UioDevice.h56
-rwxr-xr-xpython/openvino/runtime/streaming/runtime_scripts/run_image_stream.sh7
-rwxr-xr-xpython/openvino/runtime/streaming/runtime_scripts/run_inference_stream.sh14
-rw-r--r--python/openvino/runtime/streaming/streaming_inference_app/CMakeLists.txt29
-rw-r--r--python/openvino/runtime/streaming/streaming_inference_app/categories.txt1001
-rw-r--r--python/openvino/runtime/streaming/streaming_inference_app/command_line.cpp72
-rw-r--r--python/openvino/runtime/streaming/streaming_inference_app/command_line.h31
-rw-r--r--python/openvino/runtime/streaming/streaming_inference_app/streaming_inference_app.cpp413
-rw-r--r--python/openvino/runtime/streaming/streaming_inference_app/streaming_inference_app.h74
29 files changed, 4000 insertions, 0 deletions
diff --git a/python/openvino/runtime/streaming/ed0_streaming_example/README.md b/python/openvino/runtime/streaming/ed0_streaming_example/README.md
new file mode 100644
index 0000000..1cc241a
--- /dev/null
+++ b/python/openvino/runtime/streaming/ed0_streaming_example/README.md
@@ -0,0 +1,14 @@
+This directory contains an example system-console tcl script for the hostless
+streaming example design on the Agilex 7 I-series Development Kit.
+
+The system-console tcl script does the following:
+ 1. Initialize path to JTAG Avalon Master IP
+ 2. Initiates a reset via sources IP
+ 3. Writes to coreDLA's CSR registers to prime for inference
+ 4. Streams input data (img.bin) into on-chip memory via JTAG
+ 5. Writes a descriptor into egress DMA (coreDLA -> on-chip memory)
+ 6. Writes a descriptor into ingress DMA - beginning streaming process
+ from on-chip memory to DLA
+ 7. Streams output from onchip memory to output.bin via JTAG
+
+This tcl script serves as an example for a specific CNN model. To understand how this "runtime" script can be extended to support your graph, please consult the Getting Started Guide.
diff --git a/python/openvino/runtime/streaming/ed0_streaming_example/system_console_script.tcl b/python/openvino/runtime/streaming/ed0_streaming_example/system_console_script.tcl
new file mode 100644
index 0000000..ab78d2e
--- /dev/null
+++ b/python/openvino/runtime/streaming/ed0_streaming_example/system_console_script.tcl
@@ -0,0 +1,365 @@
+# This design example only supports an AXI Width of 128 bits = 16 bytes
+variable AXI_STREAM_DATA_WIDTH_BYTES 16
+# This design example has a limit to ingress on-chip memory size in bytes
+variable INGRESS_ON_CHIP_MEMORY_SIZE_BYTES 524288
+# This design example has a limit to egress on-chip memory size in bytes
+variable EGRESS_ON_CHIP_MEMORY_SIZE_BYTES 131072
+
+# DDR-Free ED Address Map Constants
+variable DLA_IP_0_CSR_ADDR 0x00038000
+variable INGRESS_SGDMA_CSR_ADDR 0x00030000
+variable INGRESS_SGDMA_DESCRIPTOR_ADDR 0x00030020
+variable EGRESS_SGDMA_CSR_ADDR 0x00030040
+variable EGRESS_SGDMA_DESCRIPTOR_ADDR 0x00030060
+
+
+# Process to validate arguments to script
+proc validate_args {input_file num_inferences} {
+ global INGRESS_ON_CHIP_MEMORY_SIZE_BYTES
+ global AXI_STREAM_DATA_WIDTH_BYTES
+ # Make sure user requested number of inferences is valid
+ if {$num_inferences < 0} {
+ puts "Number of inferences must be greater than 0."
+ exit 1
+ }
+
+ # Check if the file exists
+ if {![file exists $input_file]} {
+ puts "Error: The file '$input_file' does not exist."
+ exit 1
+ }
+
+ # Get the size of the file in bytes
+ set file_size [file size $input_file]
+
+ # Make sure the input file can fit into on-chip memory
+ if {$file_size > $INGRESS_ON_CHIP_MEMORY_SIZE_BYTES} {
+ puts "Input file '$input_file' is too large to fully fit into on-chip memory of size
+ $INGRESS_ON_CHIP_MEMORY_SIZE_BYTES bytes. Input file will be partitioned and transferred partially.\n"
+ }
+
+ # Make sure the input file is aligned to the mSGDMA/FPGA AI Suite stream width
+ if {[expr {$file_size % $AXI_STREAM_DATA_WIDTH_BYTES}] != 0} {
+ puts "Error: this design example only supports input sizes aligned to 128 bits. Please pad accordingly."
+ exit 1
+ }
+
+ # Format input file size into hex representation
+ set file_size_hex [format "0x%X" $file_size]
+
+ return $file_size
+}
+
+
+# Process to calculate # of AXI transfers that will be sent out of output streamer
+# The output streamer will send out a number of AXI transfers based on the output shape
+# H, W, C and AXI stream data width
+proc calulate_egress_axi_transfers {C H W} {
+ global EGRESS_ON_CHIP_MEMORY_SIZE_BYTES
+ global AXI_STREAM_DATA_WIDTH_BYTES
+
+ # Calculation for # of AXI transfers from output streamer
+ # # of transfers in bytes = H * W * ceil(C/8)*16
+ set output_streamer_transfers_bytes [expr {
+ $H * $W * (int(($C + 7) / 8) * 16)
+ }]
+
+ # Make sure output streamer # of transfer bytes is aligned to AXI_STREAM_DATA_WIDTH
+ if {$output_streamer_transfers_bytes <=0 || [expr {$output_streamer_transfers_bytes % $AXI_STREAM_DATA_WIDTH_BYTES}] != 0} {
+ puts "Error with egress AXI transfer calculation. Please check your output shape size arguments (C H W)"
+ exit 1
+ }
+
+ # Ensure output inference result can fit into on-chip memory
+ if {$output_streamer_transfers_bytes > $EGRESS_ON_CHIP_MEMORY_SIZE_BYTES} {
+ puts "Output inference results is too large to fully fit into on-chip memory of size
+ $EGRESS_ON_CHIP_MEMORY_SIZE_BYTES bytes. Output inference results will be partitioned and transferred partially.\n"
+ }
+ # Format input file size into hex representation
+ set output_streamer_transfers_hex [format "0x%X" $output_streamer_transfers_bytes]
+ puts "Expecting $output_streamer_transfers_hex bytes to be transferred by FPGA AI Suite output streamer"
+
+ return $output_streamer_transfers_bytes
+}
+
+
+# Initiate reset via source/probe IP
+proc assert_reset {} {
+ set issp_index 0
+ set issp [lindex [get_service_paths issp] 0]
+ set claimed_issp [claim_service issp $issp mylib]
+ set source_data 0x0
+ issp_write_source_data $claimed_issp $source_data
+ set source_data 0x1
+ issp_write_source_data $claimed_issp $source_data
+}
+
+
+# Initializing coreDLA (register map: fpga/csr/rtl/inc/dla_csr_constants.svh)
+proc initialize_coredla {master_path} {
+ global DLA_IP_0_CSR_ADDR
+ global INGRESS_SGDMA_CSR_ADDR
+ global EGRESS_SGDMA_CSR_ADDR
+
+ set csr_register_addr [expr {$DLA_IP_0_CSR_ADDR + 0x220}]
+ master_write_32 $master_path $csr_register_addr 0
+
+ set csr_register_addr [expr {$DLA_IP_0_CSR_ADDR + 0x204}]
+ master_write_32 $master_path $csr_register_addr 0
+
+ set csr_register_addr [expr {$DLA_IP_0_CSR_ADDR + 0x200}]
+ master_write_32 $master_path $csr_register_addr 3
+
+ # Writing 0x1 to this register will instruct DLA to accept input until register is cleared
+ set csr_register_addr [expr {$DLA_IP_0_CSR_ADDR + 0x22c}]
+ master_write_32 $master_path $csr_register_addr 1
+
+ # Reset egress SGDMA
+ set csr_register_addr [expr {$EGRESS_SGDMA_CSR_ADDR + 0x4}]
+ master_write_32 $master_path $csr_register_addr 0x2
+
+ # Reset ingress SGDMA
+ set csr_register_addr [expr {$INGRESS_SGDMA_CSR_ADDR + 0x4}]
+ master_write_32 $master_path $csr_register_addr 0x2
+}
+
+
+proc stage_input {input_file master_path} {
+ # Initializing rom with input image
+ master_write_from_file $master_path $input_file 0x00200000
+}
+
+
+# Adding descriptor to egress streaming mSGDMA
+proc queue_egress_descriptor {master_path size} {
+ global EGRESS_SGDMA_DESCRIPTOR_ADDR
+
+ # Destination addr
+ set csr_register_addr [expr {$EGRESS_SGDMA_DESCRIPTOR_ADDR + 0x4}]
+ master_write_32 $master_path $csr_register_addr 0x00280000
+
+ # Length should be 128 bit aligned
+ set csr_register_addr [expr {$EGRESS_SGDMA_DESCRIPTOR_ADDR + 0x8}]
+ master_write_32 $master_path $csr_register_addr $size
+
+ # Queue descriptor (Writing 0x8000_0000)
+ set csr_register_addr [expr {$EGRESS_SGDMA_DESCRIPTOR_ADDR + 0xc}]
+ master_write_32 $master_path $csr_register_addr 0x80000000
+}
+
+
+# Adding descriptor to ingress streaming mSGDMA
+proc queue_ingress_descriptor {master_path size} {
+ global INGRESS_SGDMA_DESCRIPTOR_ADDR
+
+ # Source addr
+ master_write_32 $master_path $INGRESS_SGDMA_DESCRIPTOR_ADDR 0x00200000
+
+ # Transfer length in bytes (input size)
+ set csr_register_addr [expr {$INGRESS_SGDMA_DESCRIPTOR_ADDR + 0x8}]
+ master_write_32 $master_path $csr_register_addr $size
+
+ # Queue descriptor
+ set csr_register_addr [expr {$INGRESS_SGDMA_DESCRIPTOR_ADDR + 0xc}]
+ master_write_32 $master_path $csr_register_addr 0x80000000
+}
+
+
+# Read output from on-chip memory
+proc read_output {master_path output_file size} {
+ master_read_to_file $master_path $output_file 0x00280000 $size
+}
+
+
+# Read output from on-chip memory
+proc check_inference_count {master_path iteration} {
+ global DLA_IP_0_CSR_ADDR
+ # Completion counter assert from index
+ set completion_counter_assert 0x00000000
+ set completion_counter_assert [expr {$completion_counter_assert + $iteration}]
+ set formatted_counter_assert [format "0x%08X" $completion_counter_assert]
+
+ # Check what completion counter CSR in HW is set to
+ set csr_register_addr [expr {$DLA_IP_0_CSR_ADDR + 0x224}]
+ set completion_counter_result [master_read_32 $master_path $csr_register_addr 1]
+ puts "Completion counter from HW: $completion_counter_result"
+ if {$completion_counter_result != $formatted_counter_assert} {
+ error "Error: completion counter should be equal to $formatted_counter_assert but instead is $completion_counter_result"
+ }
+}
+
+
+# This process handles creating a binary file from input partition data
+proc create_input_bin {partition_data index} {
+ set temp_file "chunk_$index.bin"
+ set temp_fh [open $temp_file "wb"]
+ fconfigure $temp_fh -translation binary
+ puts -nonewline $temp_fh $partition_data
+ close $temp_fh
+ return $temp_file
+}
+
+
+# This process polls a register and returns if assertion is true within a timeout window
+proc poll_register {master_path register_addr register_val_assert} {
+ # Set timeout to be 30 seconds (in centi-seconds)
+ set timeout_count 3000
+ while {$timeout_count > 0} {
+ set register_val [master_read_32 $master_path $register_addr 1]
+ if {$register_val == $register_val_assert} {
+ break
+ }
+ set timeout_count [expr {$timeout_count - 1}]
+ after 10
+ }
+ if {$timeout_count == 0} {
+ puts "Register polling timeout. CSR addr: $register_addr = $register_val \nRegister should be = $register_val_assert"
+ exit 1
+ }
+}
+
+
+# Printing usage process
+proc print_usage {} {
+ puts "Usage: system-console --script system_console_script.tcl <input.bin file> <# of inferences>
+ <output channels> <output height> <output width>"
+ exit 1
+}
+
+
+# Main Function
+proc main {argc argv} {
+ global INGRESS_ON_CHIP_MEMORY_SIZE_BYTES
+ global EGRESS_ON_CHIP_MEMORY_SIZE_BYTES
+ global AXI_STREAM_DATA_WIDTH_BYTES
+ global INGRESS_SGDMA_DESCRIPTOR_ADDR
+ global EGRESS_SGDMA_DESCRIPTOR_ADDR
+ global INGRESS_SGDMA_CSR_ADDR
+ global EGRESS_SGDMA_CSR_ADDR
+
+ # Check if the script should display help information
+ if {$argc > 0} {
+ set firstArg [lindex $argv 0]
+ if {[string equal $firstArg "help"] || [string equal $firstArg "--help"] || [string equal $firstArg "-help"]} {
+ print_usage
+ }
+ }
+
+ # Check the total number of arguments
+ if {$argc != 5} {
+ print_usage
+ }
+
+ # Setting script arguments to variables
+ set input_file [lindex $argv 0]
+ set num_inferences [lindex $argv 1]
+ set C [lindex $argv 2]
+ set H [lindex $argv 3]
+ set W [lindex $argv 4]
+
+ # Validating script arguments. Return input file size in bytes
+ set file_size [validate_args $input_file $num_inferences]
+ set file_size_hex [format "0x%X" $file_size]
+
+ # Calculate # of AXI transfers from FPGA AI Suite IP output streamer in bytes
+ set output_streamer_transfers [calulate_egress_axi_transfers $C $H $W]
+
+ puts "\nInput file provided: $input_file and is of size $file_size_hex bytes"
+ puts "Number of inferences: $num_inferences"
+
+ # Claim service path to System Console
+ set mpath [lindex [get_service_paths master] 0]
+ set master_path [claim_service master $mpath ""]
+
+ puts "\n________________________________________________________________________________"
+ puts " STARTING FPGA AI SUITE INFERENCE "
+ puts "________________________________________________________________________________\n"
+
+ # Assert resetn using source/probe IP
+ assert_reset
+ # Initialize coreDLA's CSR registers
+ initialize_coredla $master_path
+
+ # Open the input binary file for reading
+
+ for {set i 1} {$i <= $num_inferences} {incr i} {
+ # Open input file per iteration due to the potential partioning in the case where input file > INGRESS_ON_CHIP_MEMORY_SIZE_BYTES.
+ set input_fh [open $input_file "rb"]
+ fconfigure $input_fh -translation binary
+
+ # Create an output file every iteration of inferences
+ set combined_fh [open "output$i.bin" "wb"]
+ fconfigure $combined_fh -translation binary
+
+ # Logic to ensure input image can fully fit into ingress on-chip memory
+ # If not, must partition input data into chunks at a time. This allows us to queue
+ # descriptors for partial input sizes.
+ set num_input_partition [expr {int(($file_size + $INGRESS_ON_CHIP_MEMORY_SIZE_BYTES - 1) / $INGRESS_ON_CHIP_MEMORY_SIZE_BYTES)}]
+ for {set j 0} {$j < $num_input_partition} {incr j} {
+ set offset [expr {$j * $INGRESS_ON_CHIP_MEMORY_SIZE_BYTES}]
+ set size [
+ expr {($file_size - $offset) < $INGRESS_ON_CHIP_MEMORY_SIZE_BYTES ? ($file_size - $offset) : $INGRESS_ON_CHIP_MEMORY_SIZE_BYTES}
+ ]
+
+ # Seek to the offset and read the chunk
+ # Need to catch an error if offset > file size
+ if {[catch {seek $input_fh $offset} err]} {
+ puts "Error seeking to offset $offset: $err"
+ close $input_fh
+ exit 1
+ }
+
+ # Begin partioning the input data to INGRESS_ON_CHIP_MEMORY_SIZE_BYTES chunks
+ set partition_data [read $input_fh $size]
+ set partition_data_file_name [create_input_bin $partition_data $j]
+ stage_input $partition_data_file_name $master_path
+ queue_ingress_descriptor $master_path $size
+ file delete $partition_data_file_name
+
+ # Poll SGDMA register to check if input data streaming is complete
+ set sgdma_csr_assert 0x00000002
+ poll_register $master_path $INGRESS_SGDMA_CSR_ADDR $sgdma_csr_assert
+ }
+
+ close $input_fh
+
+ # Logic to ensure output inference results can fully fit into egress on-chip memory
+ # If not, must partition output data into chunks at a time. This allows us to queue
+ # descriptors for partial output sizes.
+ set num_output_partition [expr {int(($output_streamer_transfers + $EGRESS_ON_CHIP_MEMORY_SIZE_BYTES - 1) / $EGRESS_ON_CHIP_MEMORY_SIZE_BYTES)}]
+ for {set j 0} {$j < $num_output_partition} {incr j} {
+ set offset [expr {$j * $EGRESS_ON_CHIP_MEMORY_SIZE_BYTES}]
+ set size [
+ expr {($output_streamer_transfers - $offset) < $EGRESS_ON_CHIP_MEMORY_SIZE_BYTES ? ($output_streamer_transfers - $offset) : $EGRESS_ON_CHIP_MEMORY_SIZE_BYTES}
+ ]
+ # Queue chunks of EGRESS_ON_CHIP_MEMORY_SIZE_BYTES at a time to ensure a fit in egress on-chip memory
+ queue_egress_descriptor $master_path $size
+
+ # Poll SGDMA register to check if output data streaming is complete
+ set sgdma_csr_assert 0x00000002
+ poll_register $master_path $EGRESS_SGDMA_CSR_ADDR $sgdma_csr_assert
+
+ # Write a partition of the inference result to the partition file
+ set output_file "partition_out_$j.bin"
+ read_output $master_path $output_file $size
+
+ # Open partioned output inference result
+ set bin_fh [open $output_file "rb"]
+ fconfigure $bin_fh -translation binary
+ set bin_data [read $bin_fh]
+
+ # Append smaller partition of inference result to larger output$i.bin file for inference iteration
+ puts -nonewline $combined_fh $bin_data
+ close $bin_fh
+ file delete $output_file
+ }
+ # Ensure inference count has gone up
+ check_inference_count $master_path $i
+ close $combined_fh
+ }
+
+ puts "\n$num_inferences inferences successfully completed"
+}
+
+# Main function call
+main $argc $argv \ No newline at end of file
diff --git a/python/openvino/runtime/streaming/ed0_streaming_example/system_console_script_perf.tcl b/python/openvino/runtime/streaming/ed0_streaming_example/system_console_script_perf.tcl
new file mode 100644
index 0000000..f0cd5f7
--- /dev/null
+++ b/python/openvino/runtime/streaming/ed0_streaming_example/system_console_script_perf.tcl
@@ -0,0 +1,190 @@
+# Initiate reset via source/probe IP
+proc assert_reset {} {
+ set issp_index 0
+ set issp [lindex [get_service_paths issp] 0]
+ set claimed_issp [claim_service issp $issp mylib]
+ set source_data 0x0
+ issp_write_source_data $claimed_issp $source_data
+ set source_data 0x1
+ issp_write_source_data $claimed_issp $source_data
+}
+
+# Initializing coreDLA (register map: fpga/csr/rtl/inc/dla_csr_constants.svh)
+proc initialize_coredla {master_path} {
+ master_write_32 $master_path 0x00038220 0
+ master_write_32 $master_path 0x00038204 0
+ master_write_32 $master_path 0x00038200 3
+ # Writing 0x1 to this register will instruct DLA to accept input until register is cleared
+ master_write_32 $master_path 0x0003822c 1
+
+ # Reset egress descriptor
+ master_write_32 $master_path 0x00030044 0x2
+ # Stop the descriptor
+ master_write_32 $master_path 0x00030044 0x20
+
+ # Reset ingress descriptor
+ master_write_32 $master_path 0x00030004 0x2
+ # Stop the descriptor
+ master_write_32 $master_path 0x00030004 0x20
+}
+
+proc start_stream {master_path} {
+ # Start the egress descriptor
+ master_write_32 $master_path 0x00030044 0x00
+
+ # Start the ingress descriptor
+ master_write_32 $master_path 0x00030004 0x00
+}
+
+# This checks if the descriptor buffers are full
+proc check_descriptor_buffer_full {master_path} {
+ set egress_descriptor_status [master_read_32 $master_path 0x00030040 1]
+ set ingress_descriptor_status [master_read_32 $master_path 0x00030000 1]
+
+ if {$egress_descriptor_status & 0x4} {
+ error "Egress descriptor is full."
+ }
+ if {$ingress_descriptor_status & 0x4} {
+ error "Ingress descriptor is full."
+ }
+}
+
+proc stage_input {input_file master_path} {
+ # Initializing rom with input image
+ master_write_from_file $master_path $input_file 0x00200000
+}
+
+# Adding descriptor to egress streaming mSGDMA
+proc queue_egress_descriptor {master_path} {
+ # Destination addr
+ master_write_32 $master_path 0x00030064 0x00280000
+ # Length should be 128 bit aligned
+ master_write_32 $master_path 0x00030068 0xA800
+ # Queue descriptor
+ master_write_32 $master_path 0x0003006c 0x80000000
+}
+
+# Adding descriptor to ingress streaming mSGDMA
+proc queue_ingress_descriptor {master_path} {
+ # Source addr
+ master_write_32 $master_path 0x00030020 0x00200000
+ # Transfer length in bytes (input size)
+ master_write_32 $master_path 0x00030028 0x17A00
+ # Queue descriptor
+ master_write_32 $master_path 0x0003002c 0x80000000
+}
+
+# Copying input and output to file
+proc copy_input_for_validation {master_path} {
+ master_read_to_file $master_path input.bin 0x00200000 0x17A00
+}
+
+# Read inference counter values to get performance
+# There is an assumption here that the clk_ddr is attached to 100MHz
+proc get_performance {master_path num_inferences} {
+ set active_clk_lo [master_read_32 $master_path 0x00038240 1]
+ set active_clk_hi [master_read_32 $master_path 0x00038244 1]
+ set total_active_clk_count [expr { $active_clk_lo | ($active_clk_hi << 32) }]
+ set active_clk_count_per_inference [expr {$total_active_clk_count / $num_inferences}]
+ puts "Total active clk cycles: 0x$total_active_clk_count"
+
+ set all_active_clk_lo [master_read_32 $master_path 0x00038248 1]
+ set all_active_clk_hi [master_read_32 $master_path 0x0003824c 1]
+ set all_active_clk_count [expr { $all_active_clk_lo | ($all_active_clk_hi << 32) }]
+ set all_active_clk_count_per_inference [expr {$all_active_clk_count / $num_inferences}]
+
+ set core_active_clk_lo [master_read_32 $master_path 0x0003827c 1]
+ set core_active_clk_hi [master_read_32 $master_path 0x00038280 1]
+ set total_core_active_clk_count [expr { $core_active_clk_lo | ($core_active_clk_hi << 32) }]
+ set core_active_clk_count_per_inference [expr {$total_core_active_clk_count / $num_inferences}]
+ puts "Total core active clk cycles (without input and output streamer): 0x$total_core_active_clk_count"
+
+ set clk_period [expr { 1.0 / 100000000.0 }]
+ set final_fps [expr { 1 / ($clk_period * $active_clk_count_per_inference) }]
+ set final_latency [expr { 1 / ($clk_period * $all_active_clk_count_per_inference) }]
+
+ puts "--------------------------------------------------------"
+ puts "Final Throughput: $final_fps fps assuming 100MHz clk_ddr"
+}
+
+# Poll the completion counter until it reaches the expected number of inferences
+proc wait_for_completion_counter {master_path num_inferences} {
+ # Set timeout to be 30 seconds (in centi-seconds)
+ set timeout_count 3000
+ while {$timeout_count > 0} {
+ set completion_counter_result [master_read_32 $master_path 0x00038224 1]
+ if {$completion_counter_result == $num_inferences} {
+ break
+ }
+ set timeout_count [expr {$timeout_count - 1}]
+ after 10
+ }
+ if {$timeout_count == 0} {
+ error "Timeout hit at 30 seconds. Increase the timeout if the inference is expected to take longer."
+ }
+}
+
+# Read output from on-chip memory
+proc read_last_output {master_path num_inference} {
+ # Completion counter assert form index
+ set completion_counter_assert 0x00000000
+ set completion_counter_assert [expr {$completion_counter_assert + $num_inference}]
+ set formatted_counter_assert [format "0x%08X" $completion_counter_assert]
+
+ # Check what completion counter CSR in HW is set to
+ set completion_counter_result [master_read_32 $master_path 0x00038224 1]
+ puts "Completion counter from HW: $completion_counter_result"
+ if {$completion_counter_result == $formatted_counter_assert} {
+ master_read_to_file $master_path output0.bin 0x00280000 0xA800
+ } else {
+ error "Error: completion counter should be equal to $formatted_counter_assert but instead is $completion_counter_result"
+ }
+}
+# This design example has a limit to ingress on-chip memory size in bytes
+set INGRESS_ON_CHIP_MEMORY_SIZE_BYTES 524288
+
+# Main Function
+if {$argc != 1} {
+ error "Usage: system-console --script system_console_script_perf.tcl <input.bin file>"
+}
+set input_file [lindex $argv 0]
+puts "Input file provided: $input_file"
+
+set file_size [file size $input_file]
+
+# Make sure the input file can fit into on-chip memory
+if {$file_size > $INGRESS_ON_CHIP_MEMORY_SIZE_BYTES} {
+ puts "Input file '$input_file' is too large to fully fit into on-chip memory of size
+ $INGRESS_ON_CHIP_MEMORY_SIZE_BYTES bytes.\nThe `system_console_script.tcl` file will
+ partition the input file for partial transfers to solve this problem but it should not
+ be used for performance testing. Please increase the on-chip memory size for performance
+ testing.\n"
+ exit 1
+}
+
+set mpath [lindex [get_service_paths master] 0]
+set master_path [claim_service master $mpath ""]
+
+# Assert resetn using source/probe IP
+assert_reset
+# Stage input file into on-chip memory
+stage_input $input_file $master_path
+# Initialize coreDLA's CSR registers
+initialize_coredla $master_path
+
+# Number of inferences cannot exceed the descriptor queue FIFO size
+set num_inferences 32
+for {set i 1} {$i <= $num_inferences} {incr i} {
+ check_descriptor_buffer_full $master_path
+ # Queue egress descriptor into mSGDMA
+ queue_egress_descriptor $master_path
+ # Queue egress descriptor into mSGDMA
+ queue_ingress_descriptor $master_path
+}
+
+start_stream $master_path
+wait_for_completion_counter $master_path $num_inferences
+get_performance $master_path $num_inferences
+read_last_output $master_path $num_inferences
+
+puts "\n$num_inferences inferences successfully completed"
diff --git a/python/openvino/runtime/streaming/image_streaming_app/CMakeLists.txt b/python/openvino/runtime/streaming/image_streaming_app/CMakeLists.txt
new file mode 100644
index 0000000..4ac6627
--- /dev/null
+++ b/python/openvino/runtime/streaming/image_streaming_app/CMakeLists.txt
@@ -0,0 +1,36 @@
+# Copyright 2023 Intel Corporation
+
+# This software and the related documents are Intel copyrighted materials,
+# and your use of them is governed by the express license under which they
+# were provided to you ("License"). Unless the License provides otherwise,
+# you may not use, modify, copy, publish, distribute, disclose or transmit
+# this software or the related documents without Intel's prior written
+# permission.
+
+# This software and the related documents are provided as is, with no express
+# or implied warranties, other than those that are expressly stated in the
+# License.
+
+project(image_streaming_app)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+set(all_files ${header_files}
+ command_line.cpp
+ command_line.h
+ image_streaming_app.cpp
+ image_streaming_app.h
+ raw_image.cpp
+ raw_image.h
+ bmp_file.cpp
+ bmp_file.h
+ float16.h)
+
+# Targets
+add_executable(${PROJECT_NAME} ${all_files})
+
+add_subdirectory(uio)
+add_subdirectory(layout_transform)
+
+target_link_libraries(${PROJECT_NAME} layout_transform)
diff --git a/python/openvino/runtime/streaming/image_streaming_app/bmp_file.cpp b/python/openvino/runtime/streaming/image_streaming_app/bmp_file.cpp
new file mode 100644
index 0000000..6502897
--- /dev/null
+++ b/python/openvino/runtime/streaming/image_streaming_app/bmp_file.cpp
@@ -0,0 +1,277 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#include "bmp_file.h"
+#include <cstdlib>
+#include <cstring>
+#include <fstream>
+
+BmpFile::BmpFile(const std::string& filename, bool planarBGR) {
+ bool loaded = LoadFile(filename, planarBGR);
+ (void)loaded;
+}
+
+bool BmpFile::LoadFile(const std::string& filename, bool planarBGR) {
+ std::ifstream inputFile(filename, std::fstream::binary);
+ if (inputFile.bad()) {
+ return false;
+ }
+
+ // Read signature
+ uint16_t fileSignature = 0;
+ if (!inputFile.read((char*)&fileSignature, sizeof(fileSignature))) {
+ return false;
+ }
+
+ if (fileSignature != 0x4d42) {
+ return false;
+ }
+
+ // Read file size
+ uint32_t fileSize = 0;
+ if (!inputFile.read((char*)&fileSize, sizeof(fileSize))) {
+ return false;
+ }
+
+ if (fileSize > (8192 * 4320 * 3)) { // Check excessive file size
+ return false;
+ }
+
+ // Reserved
+ uint32_t unused = 0;
+ if (!inputFile.read((char*)&unused, sizeof(unused))) {
+ return false;
+ }
+
+ // Read data offset
+ uint32_t dataOffset = 0;
+ if (!inputFile.read((char*)&dataOffset, sizeof(dataOffset))) {
+ return false;
+ }
+
+ if ((dataOffset >= fileSize) or (dataOffset == 0)) {
+ return false;
+ }
+
+ // Read bitmap header
+ BitmapHeader infoHeader{};
+ if (!inputFile.read((char*)&infoHeader, sizeof(infoHeader))) {
+ return false;
+ }
+
+ uint32_t headerSize = sizeof(infoHeader);
+ uint32_t header4Size = 108; // sizeof(BITMAPV4HEADER);
+ uint32_t header5Size = 124; // sizeof(BITMAPV5HEADER);
+ if ((infoHeader._size != headerSize) and (infoHeader._size != header4Size) and (infoHeader._size != header5Size)) {
+ return false;
+ }
+
+ int palletteSize = infoHeader._colorUsed;
+ std::vector<uint32_t> pallette;
+ if ((infoHeader._bitCount < 16) and (infoHeader._colorUsed == 0) and (infoHeader._bitCount != 1)) {
+ palletteSize = 1 << infoHeader._bitCount;
+ }
+
+ if (palletteSize > 0) {
+ // V3 Pallette follows 4 bytes per entry
+ pallette.resize(palletteSize);
+ if (!inputFile.read((char*)pallette.data(), pallette.size())) {
+ return false;
+ }
+ }
+
+ inputFile.seekg(dataOffset);
+
+ uint32_t height = static_cast<uint32_t>(std::abs(infoHeader._height));
+ size_t dataSize = static_cast<size_t>(infoHeader._sizeImage);
+ uint32_t nPixels = height * static_cast<uint32_t>(infoHeader._width);
+
+ if (infoHeader._bitCount == 32) {
+ dataSize = height * infoHeader._width * 4;
+ } else if (infoHeader._bitCount == 16) {
+ dataSize = height * infoHeader._width * 2;
+ } else if (infoHeader._bitCount == 8) {
+ if (dataSize == 0) dataSize = height * infoHeader._width; // 8 bit data - through pallette
+ } else {
+ uint32_t line_length = infoHeader._width;
+ if ((infoHeader._bitCount == 24) and ((infoHeader._width % 4) != 0)) {
+ line_length = (infoHeader._width + 4) & ~3;
+ }
+ dataSize = height * line_length * 3;
+ }
+
+ std::vector<uint8_t> _temporaryBuffer;
+ bool useTemporaryBuffer = (infoHeader._bitCount == 16) or (infoHeader._bitCount == 1) or (palletteSize > 0);
+
+ if (useTemporaryBuffer) {
+ _temporaryBuffer.resize(dataSize);
+ if (!inputFile.read((char*)_temporaryBuffer.data(), dataSize)) return false;
+ } else {
+ _data.resize(dataSize);
+ if (!inputFile.read((char*)_data.data(), dataSize)) return false;
+ }
+
+ if (infoHeader._bitCount == 16) {
+ int inputStride = infoHeader._sizeImage / height;
+
+ dataSize = nPixels * 4;
+ _data.resize(dataSize);
+ uint32_t* pOutputScan = reinterpret_cast<uint32_t*>(_data.data());
+
+ for (uint32_t y = 0; y < height; y++) {
+ uint8_t* pInputLineStart = _temporaryBuffer.data() + (y * inputStride);
+ uint16_t* pInputScan = (uint16_t*)pInputLineStart;
+
+ for (int x = 0; x < infoHeader._width; x++) {
+ uint16_t inputValue = *pInputScan++;
+ uint32_t r = ((inputValue & 0x7C00) >> 10) * 8;
+ uint32_t g = ((inputValue & 0x3E0) >> 5) * 8;
+ uint32_t b = ((inputValue & 0x1f) * 8);
+
+ *pOutputScan++ = 0xff000000 | r << 16 | g << 8 | b;
+ }
+ }
+
+ infoHeader._bitCount = 32;
+ } else if (infoHeader._bitCount == 1) {
+ int inputStride = infoHeader._sizeImage / height;
+
+ dataSize = nPixels * 4;
+ _data.resize(dataSize);
+ uint32_t* pOutputScan = reinterpret_cast<uint32_t*>(_data.data());
+
+ for (uint32_t y = 0; y < height; y++) {
+ uint8_t* pInputLineStart = _temporaryBuffer.data() + (y * inputStride);
+ uint8_t* pInputScan = pInputLineStart;
+
+ uint16_t inputValue = *pInputScan++;
+ for (int x = 0; x < infoHeader._width; x++) {
+ int bit = x % 8;
+ if (bit == 0) {
+ inputValue = *pInputScan++;
+ }
+
+ int bit_mask = 1 << (7 - bit);
+
+ if ((inputValue & bit_mask) == 0)
+ *pOutputScan++ = 0xff000000;
+ else
+ *pOutputScan++ = 0xffffffff;
+ }
+ }
+
+ infoHeader._bitCount = 32;
+ }
+
+ if (palletteSize > 0) {
+ // we're using a pallette - convert _buffer using pallette
+ _data.resize(dataSize * sizeof(uint32_t));
+ uint32_t* pOutputScan = reinterpret_cast<uint32_t*>(_data.data());
+ infoHeader._bitCount = 32; // pretend were now 32 bits as that is format of Pallette
+ for (size_t i = 0; i < dataSize; i++) {
+ *pOutputScan++ = pallette[_temporaryBuffer[i]];
+ }
+ }
+
+ _height = height;
+ _width = infoHeader._width;
+ _bitsPerPixel = infoHeader._bitCount;
+
+ uint32_t lineLengthBytes = (_width * _bitsPerPixel) / 8;
+
+ if ((_bitsPerPixel == 24) and ((lineLengthBytes % 4) != 0)) {
+ _stride = (lineLengthBytes + 4) & ~3;
+ } else {
+ _stride = lineLengthBytes;
+ }
+
+ _upsideDown = (infoHeader._height > 0);
+
+ // BMP channel order is BGR, as required by ResNet
+ if (_upsideDown) {
+ std::vector<uint8_t> flippedData(_data.size());
+ for (uint32_t y = 0; y < _height; y++) {
+ uint8_t* pDestinationLine = flippedData.data() + (y * _stride);
+ uint8_t* pSourceLine = _data.data() + ((_height - y - 1) * _stride);
+
+ std::memcpy(pDestinationLine, pSourceLine, _stride);
+ }
+
+ _data = flippedData;
+ }
+
+ if (planarBGR) {
+ uint32_t channelSize = _width * _height;
+ std::vector<uint8_t> planarData(_data.size());
+ uint8_t* pBPlane = planarData.data();
+ uint8_t* pGPlane = pBPlane + channelSize;
+ uint8_t* pRPlane = pGPlane + channelSize;
+ uint8_t* pInputBGR = _data.data();
+
+ for (uint32_t i = 0; i < channelSize; i++) {
+ *pBPlane++ = *pInputBGR++;
+ *pGPlane++ = *pInputBGR++;
+ *pRPlane++ = *pInputBGR++;
+
+ // Skip alpha channel
+ if (infoHeader._bitCount == 32) {
+ pInputBGR++;
+ }
+ }
+
+ _data = planarData;
+ } else {
+ uint32_t channelSize = _width * _height;
+
+ // Must be 32bpp
+ if (infoHeader._bitCount == 32) {
+ // Swap endianness
+ uint8_t* pInputBGR = _data.data();
+
+ for (uint32_t i = 0; i < channelSize; i++) {
+ uint8_t b = pInputBGR[0];
+ uint8_t g = pInputBGR[1];
+ uint8_t r = pInputBGR[2];
+ uint8_t a = pInputBGR[3];
+
+ pInputBGR[0] = a;
+ pInputBGR[1] = r;
+ pInputBGR[2] = g;
+ pInputBGR[3] = b;
+
+ pInputBGR += 4;
+ }
+ } else {
+ std::vector<uint8_t> newData(channelSize * 4);
+ uint8_t* pInputBGR = _data.data();
+ uint8_t* pOutputBGRA = newData.data();
+ for (uint32_t i = 0; i < channelSize; i++) {
+ uint8_t b = pInputBGR[0];
+ uint8_t g = pInputBGR[1];
+ uint8_t r = pInputBGR[2];
+
+ pOutputBGRA[0] = 0;
+ pOutputBGRA[1] = r;
+ pOutputBGRA[2] = g;
+ pOutputBGRA[3] = b;
+
+ pInputBGR += 3;
+ pOutputBGRA += 4;
+ }
+
+ _data = newData;
+ }
+ }
+
+ return true;
+}
diff --git a/python/openvino/runtime/streaming/image_streaming_app/bmp_file.h b/python/openvino/runtime/streaming/image_streaming_app/bmp_file.h
new file mode 100644
index 0000000..95ab306
--- /dev/null
+++ b/python/openvino/runtime/streaming/image_streaming_app/bmp_file.h
@@ -0,0 +1,47 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#pragma once
+#include <cstdint>
+#include <string>
+#include <vector>
+
+struct BitmapHeader {
+ uint32_t _size;
+ int32_t _width;
+ int32_t _height;
+ uint16_t _planes;
+ uint16_t _bitCount;
+ uint32_t _compression;
+ uint32_t _sizeImage;
+ int32_t _xPixelsPerMeter;
+ int32_t _yPixelsPerMeter;
+ uint32_t _colorUsed;
+ uint32_t _colorImportant;
+};
+
+class BmpFile {
+ public:
+ BmpFile(const std::string& filename, bool planarBGR);
+ std::vector<uint8_t>& GetData() { return _data; }
+ uint32_t GetNumPixels() { return (_width * _height); }
+
+ private:
+ bool LoadFile(const std::string& filename, bool planarBGR);
+ std::vector<uint8_t> _data;
+ uint32_t _width = 0;
+ uint32_t _height = 0;
+ uint32_t _bitsPerPixel = 0;
+ uint32_t _stride = 0;
+ bool _upsideDown = false;
+};
diff --git a/python/openvino/runtime/streaming/image_streaming_app/command_line.cpp b/python/openvino/runtime/streaming/image_streaming_app/command_line.cpp
new file mode 100644
index 0000000..794310b
--- /dev/null
+++ b/python/openvino/runtime/streaming/image_streaming_app/command_line.cpp
@@ -0,0 +1,72 @@
+// Copyright 2021-2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#include "command_line.h"
+#include <algorithm>
+
+static void TrimString(std::string& trimString) {
+ trimString.erase(0, trimString.find_first_not_of(" \n\r\t"));
+ trimString.erase(trimString.find_last_not_of(" \n\r\t") + 1);
+}
+
+static void MakeLower(std::string& stringValue) {
+ std::transform(stringValue.begin(), stringValue.end(), stringValue.begin(), ::tolower);
+}
+
+// Program -option=value
+CommandLine::CommandLine(int argumentCount, char* argumentValues[]) {
+ if (argumentCount > 0) _executableName = argumentValues[0];
+
+ for (int i = 1; i < argumentCount; i++) {
+ std::string inputString(argumentValues[i]);
+ std::string nextChar = inputString.substr(0, 1);
+ if ((nextChar == "-") or (nextChar == "/")) {
+ inputString = inputString.substr(1);
+ size_t equals = inputString.find("=");
+ std::string option;
+ std::string value;
+
+ if (equals == std::string::npos) {
+ option = inputString;
+ } else {
+ option = inputString.substr(0, equals);
+ value = inputString.substr(equals + 1);
+ }
+
+ TrimString(option);
+ TrimString(value);
+ MakeLower(option);
+ _optionMap[option] = value;
+ }
+ }
+}
+
+std::string CommandLine::GetOptionValue(const char* optionName) {
+ auto i = _optionMap.find(optionName);
+ if (i != _optionMap.end())
+ return i->second;
+ else
+ return "";
+}
+
+bool CommandLine::HaveOption(const char* optionName) { return (_optionMap.find(optionName) != _optionMap.end()); }
+
+bool CommandLine::GetOption(const char* optionName, std::string& optionValue) {
+ auto i = _optionMap.find(optionName);
+ if (i == _optionMap.end()) return false;
+
+ optionValue = i->second;
+ return true;
+}
+
+size_t CommandLine::NumOptions() { return _optionMap.size(); }
diff --git a/python/openvino/runtime/streaming/image_streaming_app/command_line.h b/python/openvino/runtime/streaming/image_streaming_app/command_line.h
new file mode 100644
index 0000000..41b12f0
--- /dev/null
+++ b/python/openvino/runtime/streaming/image_streaming_app/command_line.h
@@ -0,0 +1,31 @@
+// Copyright 2021-2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#pragma once
+#include <string>
+#include <unordered_map>
+
+class CommandLine {
+ public:
+ CommandLine(int argumentCount, char* argumentValues[]);
+
+ std::string GetOptionValue(const char* optionName);
+ bool GetOption(const char* optionName, std::string& optionValue);
+ bool HaveOption(const char* optionName);
+ std::string GetExecutableName() { return _executableName; }
+ size_t NumOptions();
+
+ private:
+ std::string _executableName;
+ std::unordered_map<std::string, std::string> _optionMap;
+};
diff --git a/python/openvino/runtime/streaming/image_streaming_app/float16.h b/python/openvino/runtime/streaming/image_streaming_app/float16.h
new file mode 100644
index 0000000..07b8ece
--- /dev/null
+++ b/python/openvino/runtime/streaming/image_streaming_app/float16.h
@@ -0,0 +1,204 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+/*
+Copyright (C) 2009 Apple Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ * This file helps with conversion to/from fp16. The contents of this file are duplicated
+ * in at least four places, just to ensure they are not accidentally lost:
+ *
+ * 1) runtime/plugin/io_transformations/dlia_fp.hpp.
+ * 2) hps/apps/coredla-test-harness/float16.h (Intel internal)
+ * 3) runtime/streaming/image_streaming_app/float16.h
+ * 4) util/inc/dla_element.h (internal)
+ *
+ * The algorithm for conversion to fp16 is described in this 2008 paper by Jeroen van der Zijp
+ * http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf
+ *
+ * We received this code, I am told, by way of OpenVINO. As best as I can infer from
+ * the copyright header and available bits of discussion on the web, OpenVINO seems to have
+ * copied this from Webkit.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <cstring>
+
+class Float16 {
+ public:
+ Float16(float value) {
+ // This method is used by the CoreDLA plugin, it is not binary equivalent
+ // to f32to16 (below) used by OpenVino
+ union FloatAndUint32 {
+ float _floatValue;
+ uint32_t _uint32Value;
+ };
+ FloatAndUint32 convertType;
+ convertType._floatValue = value;
+
+ uint32_t floatBits = convertType._uint32Value;
+ uint32_t shiftIndex = (floatBits >> 23) & 0x1ff;
+ _uintValue = base[shiftIndex] + ((floatBits & 0x007fffff) >> shift[shiftIndex]);
+ }
+
+ Float16() {}
+ operator uint16_t() { return _uintValue; }
+ uint16_t _uintValue;
+
+ const uint16_t base[512] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 8, 16, 32, 64, 128, 256,
+ 512, 1024, 2048, 3072, 4096, 5120, 6144, 7168, 8192, 9216, 10240, 11264, 12288, 13312, 14336, 15360,
+ 16384, 17408, 18432, 19456, 20480, 21504, 22528, 23552, 24576, 25600, 26624, 27648, 28672, 29696, 30720, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744, 31744,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32768,
+ 32768, 32768, 32768, 32768, 32768, 32768, 32768, 32769, 32770, 32772, 32776, 32784, 32800, 32832, 32896, 33024,
+ 33280, 33792, 34816, 35840, 36864, 37888, 38912, 39936, 40960, 41984, 43008, 44032, 45056, 46080, 47104, 48128,
+ 49152, 50176, 51200, 52224, 53248, 54272, 55296, 56320, 57344, 58368, 59392, 60416, 61440, 62464, 63488, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512,
+ 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512, 64512};
+
+ const uint8_t shift[512] = {
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 23, 22, 21, 20, 19,
+ 18, 17, 16, 15, 14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 13, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 13};
+
+// Function to convert F32 into F16
+// F32: exp_bias:127 SEEEEEEE EMMMMMMM MMMMMMMM MMMMMMMM.
+// F16: exp_bias:15 SEEEEEMM MMMMMMMM
+#define EXP_MASK_F32 0x7F800000U
+#define EXP_MASK_F16 0x7C00U
+
+ // small helper function to represent uint32_t value as float32
+ inline float asfloat(uint32_t v) {
+ // Both type-punning casts and unions are UB per C++ spec
+ // But compilers usually only break code with casts
+ union {
+ float f;
+ uint32_t i;
+ };
+ i = v;
+ return f;
+ }
+
+ uint16_t f32tof16_OpenVino(float x) {
+ // create minimal positive normal f16 value in f32 format
+ // exp:-14,mantissa:0 -> 2^-14 * 1.0
+ static float min16 = asfloat((127 - 14) << 23);
+
+ // create maximal positive normal f16 value in f32 and f16 formats
+ // exp:15,mantissa:11111 -> 2^15 * 1.(11111)
+ static float max16 = asfloat(((127 + 15) << 23) | 0x007FE000);
+ static uint32_t max16f16 = ((15 + 15) << 10) | 0x3FF;
+
+ // define and declare variable for intermediate and output result
+ // the union is used to simplify representation changing
+ union {
+ float f;
+ uint32_t u;
+ } v;
+ v.f = x;
+
+ // get sign in 16bit format
+ uint32_t s = (v.u >> 16) & 0x8000; // sign 16: 00000000 00000000 10000000 00000000
+
+ // make it abs
+ v.u &= 0x7FFFFFFF; // abs mask: 01111111 11111111 11111111 11111111
+
+ // check NAN and INF
+ if ((v.u & EXP_MASK_F32) == EXP_MASK_F32) {
+ if (v.u & 0x007FFFFF) {
+ return s | (v.u >> (23 - 10)) | 0x0200; // return NAN f16
+ } else {
+ return s | EXP_MASK_F16; // return INF f16
+ }
+ }
+
+ // to make f32 round to nearest f16
+ // create halfULP for f16 and add it to origin value
+ float halfULP = asfloat(v.u & EXP_MASK_F32) * asfloat((127 - 11) << 23);
+ v.f += halfULP;
+
+ // if input value is not fit normalized f16 then return 0
+ // denormals are not covered by this code and just converted to 0
+ if (v.f < min16 * 0.5F) {
+ return s;
+ }
+
+ // if input value between min16/2 and min16 then return min16
+ if (v.f < min16) {
+ return s | (1 << 10);
+ }
+
+ // if input value more than maximal allowed value for f16
+ // then return this maximal value
+ if (v.f >= max16) {
+ return max16f16 | s;
+ }
+
+ // change exp bias from 127 to 15
+ v.u -= ((127 - 15) << 23);
+
+ // round to f16
+ v.u >>= (23 - 10);
+
+ return v.u | s;
+ }
+};
diff --git a/python/openvino/runtime/streaming/image_streaming_app/image_streaming_app.cpp b/python/openvino/runtime/streaming/image_streaming_app/image_streaming_app.cpp
new file mode 100644
index 0000000..bf4cb2b
--- /dev/null
+++ b/python/openvino/runtime/streaming/image_streaming_app/image_streaming_app.cpp
@@ -0,0 +1,306 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#include "image_streaming_app.h"
+#include <signal.h>
+#include <algorithm>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <thread>
+#include <fcntl.h>
+#include "raw_image.h"
+
+int main(int numParams, char* paramValues[]) {
+ ImageStreamingApp imageStreamingApp(numParams, paramValues);
+ imageStreamingApp.Run();
+ return 0;
+}
+
+volatile bool ImageStreamingApp::_shutdownEvent;
+
+ImageStreamingApp::ImageStreamingApp(int numParams, char* paramValues[]) : _commandLine(numParams, paramValues) {
+ std::string imagesFolder;
+ if (_commandLine.GetOption("images_folder", imagesFolder))
+ _imageFilesFolder = imagesFolder;
+ else
+ _imageFilesFolder = "./";
+
+ std::string imageFile;
+ if (_commandLine.GetOption("image", imageFile)) {
+ _numToSend = 1;
+ _imageFile = imageFile;
+ }
+
+ std::string nSendStr;
+ if (_commandLine.GetOption("send", nSendStr)) _numToSend = std::strtoul(nSendStr.c_str(), 0, 0);
+
+ std::string rateStr;
+ if (_commandLine.GetOption("rate", rateStr)) _sendRate = std::strtoul(rateStr.c_str(), 0, 0);
+
+ _dumpTransformedImages = _commandLine.HaveOption("dump");
+ _runLayoutTransform = _commandLine.HaveOption("layout_transform");
+
+ _ltConfiguration._width = GetUintOption("width", 224);
+ _ltConfiguration._height = GetUintOption("height", 224);
+ _ltConfiguration._cVector = GetUintOption("c_vector", 32);
+ _ltConfiguration._blueVariance = GetFloatOption("blue_variance", 1.0f);
+ _ltConfiguration._greenVariance = GetFloatOption("green_variance", 1.0f);
+ _ltConfiguration._redVariance = GetFloatOption("red_variance", 1.0f);
+ _ltConfiguration._blueShift = GetFloatOption("blue_shift", -103.94f);
+ _ltConfiguration._greenShift = GetFloatOption("green_shift", -116.78f);
+ _ltConfiguration._redShift = GetFloatOption("red_shift", -123.68f);
+
+ signal(SIGINT, SigIntHandler);
+}
+
+void ImageStreamingApp::Run() {
+ if (_commandLine.HaveOption("-help")) {
+ std::cout << "Usage:\n";
+ std::cout << " image_streaming_app [Options]\n";
+ std::cout << "\nOptions:\n";
+ std::cout << "-images_folder=folder Location of bitmap files. Defaults to working folder.\n";
+ std::cout << "-image=path Location of a single bitmap file for single inference.\n";
+ std::cout << "-send=n Number of images to stream. Default is 1 if -image is set, otherwise infinite.\n";
+ std::cout << "-rate=n Rate to stream images, in Hz. n is an integer. Default is 30.\n";
+ std::cout << "-width=n Image width in pixels, default = 224\n";
+ std::cout << "-height=n Image height in pixels, default = 224\n";
+ std::cout << "-c_vector=n C vector size, default = 32\n";
+ std::cout << "-blue_variance=n Blue variance, default = 1.0\n";
+ std::cout << "-green_variance=n Green variance, default = 1.0\n";
+ std::cout << "-red_variance=n Red variance, default = 1.0\n";
+ std::cout << "-blue_shift=n Blue shift, default = -103.94\n";
+ std::cout << "-green_shift=n Green shift, default -116.78\n";
+ std::cout << "-red_shift=n Red shift, default = -123.68\n";
+ return;
+ }
+
+ if (not ProgramLayoutTransform()) {
+ return;
+ }
+
+ if (not LoadImageFiles(_dumpTransformedImages)) {
+ return;
+ }
+
+ if (_dumpTransformedImages) {
+ return;
+ }
+
+ if (not WaitForInferenceApp())
+ return;
+
+ // Start event signal thread
+ auto sendImageEventThreadCB = [this]() { RunSendImageSignalThread(); };
+ std::thread sendImageEventThread(sendImageEventThreadCB);
+ uint32_t sentCount = 0;
+
+ while (not _shutdownEvent) {
+ // Wait for the send image event
+ _sendNextImageEvent.Wait();
+
+ if (not SendNextImage()) {
+ _shutdownEvent = true;
+ break;
+ }
+ sentCount++;
+
+ if ((_numToSend > 0) and (sentCount >= _numToSend)) {
+ _shutdownEvent = true;
+ break;
+ }
+ }
+
+ // Wait for signalling thread to finish
+ sendImageEventThread.join();
+}
+
+bool ImageStreamingApp::LoadImageFiles(bool dumpLayoutTransform) {
+ if (not _imageFile.empty()) {
+ std::filesystem::path filePath(_imageFile);
+ std::string extension = filePath.extension();
+ std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
+ if ((extension == ".bmp") or (extension == ".raw") or (extension == ".lt")) {
+ auto spRawImage = std::make_shared<RawImage>(filePath, _runLayoutTransform, _ltConfiguration);
+ if (spRawImage->IsValid()) {
+ _images.push_back(spRawImage);
+
+ if (dumpLayoutTransform and _runLayoutTransform) {
+ spRawImage->DumpLayoutTransform();
+ }
+ } else {
+ std::cout << "Unsupported image: " << filePath << '\n';
+ }
+ }
+ } else {
+ for (const auto& entry : std::filesystem::directory_iterator(_imageFilesFolder)) {
+ std::string filename = entry.path();
+ std::filesystem::path filePath(filename);
+ std::string extension = filePath.extension();
+ std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
+ if ((extension == ".bmp") or (extension == ".raw") or (extension == ".lt")) {
+ auto spRawImage = std::make_shared<RawImage>(filePath, _runLayoutTransform, _ltConfiguration);
+ _images.push_back(spRawImage);
+
+ if (dumpLayoutTransform and _runLayoutTransform) {
+ spRawImage->DumpLayoutTransform();
+ }
+
+ // Don't load any more than we need to send
+ if (_images.size() == _numToSend) {
+ break;
+ }
+ }
+ }
+ }
+
+ std::cout << "Loaded " << _images.size() << " image";
+ if (_images.size() > 1) {
+ std::cout << "s";
+ }
+ std::cout << '\n';
+ return not _images.empty();
+}
+
+bool ImageStreamingApp::OpenMsgDmaStream() {
+ if (_msgDmaStream) {
+ return true;
+ }
+
+ constexpr const char* msgdmaFilename = "/dev/msgdma_stream0";
+ _msgDmaStream = ::fopen(msgdmaFilename, "w+");
+ if (_msgDmaStream == NULL) {
+ std::cout << "Failed to open" << '\n';
+ return false;
+ }
+
+ // Turn off output buffering
+ setvbuf(_msgDmaStream, NULL, _IONBF, 0);
+
+ return true;
+}
+
+void ImageStreamingApp::CloseMsgDmaStream() {
+ if (_msgDmaStream) {
+ ::fclose(_msgDmaStream);
+ _msgDmaStream = nullptr;
+ }
+}
+
+bool ImageStreamingApp::SendNextImage() {
+ size_t nImages = _images.size();
+ if (nImages == 0) {
+ return false;
+ }
+
+ if (not _msgDmaStream) {
+ if (not OpenMsgDmaStream()) {
+ return false;
+ }
+ }
+
+ std::shared_ptr<RawImage> uploadImage = _images[_nextImageIndex];
+
+ // Move to next index for next time
+ _nextImageIndex = (_nextImageIndex + 1) % nImages;
+ _sentCount++;
+
+ char* pBuffer = reinterpret_cast<char*>(uploadImage->GetData());
+ size_t bufferSize = uploadImage->GetSize();
+
+ std::cout << _sentCount << " Send image " << uploadImage->Filename() << " size = " << bufferSize;
+
+ size_t nWritten = ::fwrite(pBuffer, 1, bufferSize, _msgDmaStream);
+ bool ok = (nWritten == bufferSize);
+ if (ok) {
+ std::cout << '\n';
+ } else {
+ std::cout << " failed\n";
+ }
+
+ return ok;
+}
+
+void ImageStreamingApp::RunSendImageSignalThread() {
+ int64_t microSeconds = 1000000 / _sendRate;
+ if (_sendRate == 59) {
+ microSeconds = 16683; // 59.94 Hz
+ }
+
+ while (not _shutdownEvent) {
+ std::this_thread::sleep_for(std::chrono::microseconds(microSeconds));
+ _sendNextImageEvent.Set();
+ }
+}
+
+bool ImageStreamingApp::ProgramLayoutTransform() {
+ auto spLayoutTransform = ILayoutTransform::Create();
+ spLayoutTransform->SetConfiguration(_ltConfiguration);
+ return true;
+}
+
+uint32_t ImageStreamingApp::GetUintOption(const char* optionName, uint32_t defaultValue) {
+ std::string optionValue;
+ if (_commandLine.GetOption(optionName, optionValue)) {
+ return std::strtoul(optionValue.c_str(), nullptr, 0);
+ } else {
+ return defaultValue;
+ }
+}
+
+float ImageStreamingApp::GetFloatOption(const char* optionName, float defaultValue) {
+ std::string optionValue;
+ if (_commandLine.GetOption(optionName, optionValue)) {
+ return std::strtof(optionValue.c_str(), nullptr);
+ } else {
+ return defaultValue;
+ }
+}
+
+void ImageStreamingApp::SigIntHandler(int) {
+ std::cout << "\nShutting down application\n";
+ _shutdownEvent = true;
+}
+
+bool ImageStreamingApp::WaitForInferenceApp() {
+ bool isReady = false;
+ bool firstTime = true;
+ sem_t* pSemaphore = ::sem_open("/CoreDLA_ready_for_streaming", O_CREAT, 0644, 0);
+ if (!pSemaphore) {
+ return isReady;
+ }
+
+ while (not _shutdownEvent) {
+ // Don't use a wait timeout because we need to break
+ // if the user presses Ctrl+C
+ timespec waitTimeout = {};
+ int r = ::sem_timedwait(pSemaphore, &waitTimeout);
+ if (r == 0) {
+ isReady = true;
+ ::sem_post(pSemaphore);
+ break;
+ }
+
+ if (firstTime) {
+ firstTime = false;
+ std::cout << "Waiting for streaming_inference_app to become ready." << std::endl;
+ }
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ }
+
+ ::sem_close(pSemaphore);
+
+ return isReady;
+}
diff --git a/python/openvino/runtime/streaming/image_streaming_app/image_streaming_app.h b/python/openvino/runtime/streaming/image_streaming_app/image_streaming_app.h
new file mode 100644
index 0000000..0693ef8
--- /dev/null
+++ b/python/openvino/runtime/streaming/image_streaming_app/image_streaming_app.h
@@ -0,0 +1,79 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#pragma once
+#include <condition_variable>
+#include <filesystem>
+#include <vector>
+#include <semaphore.h>
+#include "ILayoutTransform.h"
+#include "command_line.h"
+
+using namespace std::chrono_literals;
+
+class RawImage;
+
+class Event {
+ public:
+ void Wait() {
+ std::unique_lock<std::mutex> lock(_signalMutex);
+ _conditionVariable.wait(lock);
+ }
+
+ void Set() {
+ std::lock_guard<std::mutex> lock(_signalMutex);
+ _conditionVariable.notify_all();
+ }
+
+ bool IsSignalled() {
+ std::unique_lock<std::mutex> lock(_signalMutex);
+ return (_conditionVariable.wait_for(lock, 0ms) != std::cv_status::timeout);
+ }
+
+ private:
+ std::mutex _signalMutex;
+ std::condition_variable _conditionVariable;
+};
+
+class ImageStreamingApp {
+ public:
+ ImageStreamingApp(int numParams, char* paramValues[]);
+ void Run();
+
+ private:
+ bool ProgramLayoutTransform();
+ bool SendNextImage();
+ bool LoadImageFiles(bool dumpLayoutTransform);
+ void RunSendImageSignalThread();
+ static void SigIntHandler(int);
+ uint32_t GetUintOption(const char* optionName, uint32_t defaultValue);
+ float GetFloatOption(const char* optionName, float defaultValue);
+ bool OpenMsgDmaStream();
+ void CloseMsgDmaStream();
+ bool WaitForInferenceApp();
+
+ CommandLine _commandLine;
+ std::filesystem::path _imageFilesFolder;
+ std::string _imageFile;
+ std::vector<std::shared_ptr<RawImage>> _images;
+ Event _sendNextImageEvent;
+ static volatile bool _shutdownEvent;
+ size_t _nextImageIndex = 0;
+ uint32_t _numToSend = 0;
+ uint32_t _sendRate = 30;
+ uint32_t _sentCount = 0;
+ bool _dumpTransformedImages = false;
+ bool _runLayoutTransform = true;
+ FILE* _msgDmaStream = nullptr;
+ ILayoutTransform::Configuration _ltConfiguration = {};
+};
diff --git a/python/openvino/runtime/streaming/image_streaming_app/layout_transform/CMakeLists.txt b/python/openvino/runtime/streaming/image_streaming_app/layout_transform/CMakeLists.txt
new file mode 100644
index 0000000..ecbffca
--- /dev/null
+++ b/python/openvino/runtime/streaming/image_streaming_app/layout_transform/CMakeLists.txt
@@ -0,0 +1,35 @@
+# Copyright 2023 Intel Corporation
+#
+# This software and the related documents are Intel copyrighted materials,
+# and your use of them is governed by the express license under which they
+# were provided to you ("License"). Unless the License provides otherwise,
+# you may not use, modify, copy, publish, distribute, disclose or transmit
+# this software or the related documents without Intel's prior written
+# permission.
+#
+# This software and the related documents are provided as is, with no express
+# or implied warranties, other than those that are expressly stated in the
+# License.
+
+project(layout_transform)
+
+set(header_files "")
+set(source_files "")
+
+# specify the C++ standard
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+# Header files
+set(header_files ${header_files} "include/ILayoutTransform.h")
+set(header_files ${header_files} "source/LayoutTransform.h")
+
+# Source files
+set(source_files ${source_files} "source/LayoutTransform.cpp")
+
+set(all_files ${header_files} ${source_files})
+
+add_library(${PROJECT_NAME} STATIC ${all_files})
+target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
+target_link_libraries(${PROJECT_NAME} uio)
+
diff --git a/python/openvino/runtime/streaming/image_streaming_app/layout_transform/include/ILayoutTransform.h b/python/openvino/runtime/streaming/image_streaming_app/layout_transform/include/ILayoutTransform.h
new file mode 100644
index 0000000..f54f9d5
--- /dev/null
+++ b/python/openvino/runtime/streaming/image_streaming_app/layout_transform/include/ILayoutTransform.h
@@ -0,0 +1,38 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#pragma once
+
+#include <cstdint>
+#include <memory>
+
+class ILayoutTransform {
+ public:
+ class Configuration {
+ public:
+ uint32_t _width;
+ uint32_t _height;
+ uint32_t _cVector;
+ float _blueVariance;
+ float _greenVariance;
+ float _redVariance;
+ float _blueShift;
+ float _greenShift;
+ float _redShift;
+ };
+
+ virtual ~ILayoutTransform() {}
+ virtual void SetConfiguration(Configuration& configuration) = 0;
+
+ static std::shared_ptr<ILayoutTransform> Create();
+};
diff --git a/python/openvino/runtime/streaming/image_streaming_app/layout_transform/source/LayoutTransform.cpp b/python/openvino/runtime/streaming/image_streaming_app/layout_transform/source/LayoutTransform.cpp
new file mode 100644
index 0000000..a045b6a
--- /dev/null
+++ b/python/openvino/runtime/streaming/image_streaming_app/layout_transform/source/LayoutTransform.cpp
@@ -0,0 +1,51 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#include "LayoutTransform.h"
+#include <thread>
+
+std::shared_ptr<ILayoutTransform> ILayoutTransform::Create() { return std::make_shared<LayoutTransform>(); }
+
+LayoutTransform::LayoutTransform() { _spUioDevice = UIO::IDevice::Load("layout_transform"); }
+
+static uint32_t FloatToUint32(float value) {
+ union FloatAndUint32 {
+ float _floatValue;
+ uint32_t _uint32Value;
+ };
+ FloatAndUint32 convertType;
+ convertType._floatValue = value;
+ return convertType._uint32Value;
+}
+
+void LayoutTransform::SetConfiguration(Configuration& configuration) {
+ _configuration = configuration;
+
+ if (_spUioDevice) {
+ _spUioDevice->Write((uint32_t)RegisterMap::RESET, 1u);
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ _spUioDevice->Write((uint32_t)RegisterMap::RESET, 0u);
+
+ _spUioDevice->Write((uint32_t)RegisterMap::C_VECT, _configuration._cVector);
+ _spUioDevice->Write((uint32_t)RegisterMap::WIDTH, _configuration._width);
+ _spUioDevice->Write((uint32_t)RegisterMap::HEIGHT, _configuration._height);
+
+ _spUioDevice->Write((uint32_t)RegisterMap::VARIANCES + 0, FloatToUint32(_configuration._blueVariance));
+ _spUioDevice->Write((uint32_t)RegisterMap::VARIANCES + 1, FloatToUint32(_configuration._greenVariance));
+ _spUioDevice->Write((uint32_t)RegisterMap::VARIANCES + 2, FloatToUint32(_configuration._redVariance));
+
+ _spUioDevice->Write((uint32_t)RegisterMap::SHIFTS + 0, FloatToUint32(_configuration._blueShift));
+ _spUioDevice->Write((uint32_t)RegisterMap::SHIFTS + 1, FloatToUint32(_configuration._greenShift));
+ _spUioDevice->Write((uint32_t)RegisterMap::SHIFTS + 2, FloatToUint32(_configuration._redShift));
+ }
+}
diff --git a/python/openvino/runtime/streaming/image_streaming_app/layout_transform/source/LayoutTransform.h b/python/openvino/runtime/streaming/image_streaming_app/layout_transform/source/LayoutTransform.h
new file mode 100644
index 0000000..3d67a5d
--- /dev/null
+++ b/python/openvino/runtime/streaming/image_streaming_app/layout_transform/source/LayoutTransform.h
@@ -0,0 +1,38 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#pragma once
+#include <memory>
+#include "ILayoutTransform.h"
+#include "IUioDevice.h"
+
+enum class RegisterMap : uint32_t {
+ RESET = 0,
+ C_VECT,
+ WIDTH,
+ HEIGHT,
+ VARIANCES = 0x10, // to 0x1f
+ SHIFTS = 0x20, // to 0x2f
+};
+
+class LayoutTransform : public ILayoutTransform {
+ public:
+ LayoutTransform();
+
+ // ILayoutTransform interface
+ void SetConfiguration(Configuration& configuration) override;
+
+ private:
+ ILayoutTransform::Configuration _configuration = {};
+ std::shared_ptr<UIO::IDevice> _spUioDevice;
+};
diff --git a/python/openvino/runtime/streaming/image_streaming_app/raw_image.cpp b/python/openvino/runtime/streaming/image_streaming_app/raw_image.cpp
new file mode 100644
index 0000000..bd5658b
--- /dev/null
+++ b/python/openvino/runtime/streaming/image_streaming_app/raw_image.cpp
@@ -0,0 +1,225 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#include "raw_image.h"
+#include <algorithm>
+#include <filesystem>
+#include <fstream>
+
+std::vector<int32_t> RawImage::_indexes;
+
+RawImage::RawImage(std::filesystem::path filePath,
+ bool runLayoutTransform,
+ const ILayoutTransform::Configuration& ltConfiguration)
+ : _filePath(filePath), _ltConfiguration(ltConfiguration) {
+ std::string extension = filePath.extension();
+ std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
+ if (extension == ".lt") {
+ uintmax_t fileSize = std::filesystem::file_size(filePath);
+ std::ifstream layoutFile(filePath, std::fstream::binary);
+ _layoutTransformData.resize(fileSize / sizeof(uint16_t));
+ layoutFile.read((char*)_layoutTransformData.data(), fileSize);
+ } else {
+ bool planar = runLayoutTransform;
+ _spBmpFile = std::make_shared<BmpFile>(filePath, planar);
+ if (_runLayoutTransform) LayoutTransform(_ltConfiguration);
+ }
+}
+
+uint8_t* RawImage::GetData() {
+ if (_runLayoutTransform)
+ return reinterpret_cast<uint8_t*>(_layoutTransformData.data());
+ else
+ return _spBmpFile->GetData().data();
+}
+
+size_t RawImage::GetSize() {
+ if (_runLayoutTransform)
+ return _layoutTransformData.size() * sizeof(uint16_t);
+ else
+ return _spBmpFile->GetData().size();
+}
+
+bool RawImage::IsValid()
+{
+ constexpr size_t dlaImageSize = 224 * 224 * 4;
+ return (GetSize() == dlaImageSize);
+}
+
+std::vector<uint16_t> RawImage::LayoutTransform(uint32_t width,
+ uint32_t height,
+ const std::vector<uint8_t>& sourceData,
+ const ILayoutTransform::Configuration& ltConfiguration) {
+ uint32_t numPixels = width * height;
+ std::vector<uint16_t> layoutTransformData = LayoutTransform(sourceData, numPixels, ltConfiguration);
+ return layoutTransformData;
+}
+
+void RawImage::LayoutTransform(const ILayoutTransform::Configuration& ltConfiguration) {
+ const std::vector<uint8_t>& sourceData = _spBmpFile->GetData();
+ uint32_t numPixels = _spBmpFile->GetNumPixels();
+ _layoutTransformData = LayoutTransform(sourceData, numPixels, ltConfiguration);
+}
+
+std::vector<uint16_t> RawImage::LayoutTransform(const std::vector<uint8_t>& sourceData,
+ uint32_t numPixels,
+ const ILayoutTransform::Configuration& ltConfiguration) {
+ if (_indexes.empty()) GenerateLayoutIndexes(ltConfiguration);
+
+ uint32_t numChannels = 3;
+ uint32_t numSamples = numPixels * numChannels;
+
+ std::vector<uint16_t> meanAdjustedData(numSamples);
+ const uint8_t* pSourceData = sourceData.data();
+
+ const uint8_t* pBlueSourcePlane = pSourceData;
+ const uint8_t* pGreenSourcePlane = pBlueSourcePlane + numPixels;
+ const uint8_t* pRedSourcePlane = pGreenSourcePlane + numPixels;
+
+ // First adjust by subtracting the means values
+ std::vector<float> meanAdjustedFloat32(numSamples);
+ float* pBlueFloat32 = &meanAdjustedFloat32[0];
+ float* pGreenFloat32 = pBlueFloat32 + numPixels;
+ float* pRedFloat32 = pGreenFloat32 + numPixels;
+
+ for (uint32_t i = 0; i < numPixels; i++) {
+ *pBlueFloat32++ = static_cast<float>(*pBlueSourcePlane++) + ltConfiguration._blueShift;
+ *pGreenFloat32++ = static_cast<float>(*pGreenSourcePlane++) + ltConfiguration._greenShift;
+ *pRedFloat32++ = static_cast<float>(*pRedSourcePlane++) + ltConfiguration._redShift;
+ }
+
+ pBlueFloat32 = &meanAdjustedFloat32[0];
+ pGreenFloat32 = pBlueFloat32 + numPixels;
+ pRedFloat32 = pGreenFloat32 + numPixels;
+ uint16_t* pBlueDestinationPlane = &meanAdjustedData[0];
+ uint16_t* pGreenDestinationPlane = pBlueDestinationPlane + numPixels;
+ uint16_t* pRedDestinationPlane = pGreenDestinationPlane + numPixels;
+
+ for (uint32_t i = 0; i < numPixels; i++) {
+ *pBlueDestinationPlane++ = Float16(*pBlueFloat32++);
+ *pGreenDestinationPlane++ = Float16(*pGreenFloat32++);
+ *pRedDestinationPlane++ = Float16(*pRedFloat32++);
+ }
+
+ // Now map the data to the layout expected by the DLA
+ size_t nLayoutEntries = _indexes.size();
+ std::vector<uint16_t> layoutTransformData(nLayoutEntries);
+
+ for (size_t outputIndex = 0; outputIndex < nLayoutEntries; outputIndex++) {
+ int32_t inputIndex = _indexes[outputIndex];
+ if (inputIndex >= 0)
+ layoutTransformData[outputIndex] = meanAdjustedData[inputIndex];
+ else
+ layoutTransformData[outputIndex] = 0;
+ }
+
+ return layoutTransformData;
+}
+
+bool RawImage::DumpLayoutTransform() {
+ if (!_spBmpFile) return false;
+
+ std::filesystem::path filePath(_filePath);
+ filePath.replace_extension("raw");
+ std::ofstream rawRgbaFile(filePath, std::fstream::binary);
+ if (rawRgbaFile.bad()) return false;
+
+ uint32_t numPixels = _spBmpFile->GetNumPixels();
+ uint32_t numChannels = 4;
+ uint32_t numSamples = numPixels * numChannels;
+ std::vector<uint8_t> buffer(numSamples);
+ uint8_t* pSourceData = _spBmpFile->GetData().data();
+
+ uint8_t* pBlueSourcePlane = pSourceData;
+ uint8_t* pGreenSourcePlane = pBlueSourcePlane + numPixels;
+ uint8_t* pRedSourcePlane = pGreenSourcePlane + numPixels;
+ uint8_t* pDestination = buffer.data();
+
+ for (uint32_t i = 0; i < numPixels; i++) {
+ *pDestination++ = *pBlueSourcePlane++;
+ *pDestination++ = *pGreenSourcePlane++;
+ *pDestination++ = *pRedSourcePlane++;
+ *pDestination++ = 0;
+ }
+
+ rawRgbaFile.write((char*)buffer.data(), buffer.size());
+
+ filePath.replace_extension("lt");
+ std::ofstream transformFile(filePath, std::fstream::binary);
+ if (transformFile.bad()) return false;
+
+ transformFile.write((char*)GetData(), GetSize());
+
+ return true;
+}
+
+// Convert from RGBA to planar BGR
+std::vector<uint8_t> RawImage::MakePlanar(uint32_t width, uint32_t height, const std::vector<uint8_t>& data) {
+ uint32_t channelSize = width * height;
+ std::vector<uint8_t> planarData(channelSize * 3);
+ uint8_t* pBPlane = planarData.data();
+ uint8_t* pGPlane = pBPlane + channelSize;
+ uint8_t* pRPlane = pGPlane + channelSize;
+ const uint8_t* pInputRGBA = data.data();
+
+ for (uint32_t i = 0; i < channelSize; i++) {
+ *pRPlane++ = *pInputRGBA++;
+ *pGPlane++ = *pInputRGBA++;
+ *pBPlane++ = *pInputRGBA++;
+
+ // Skip alpha channel
+ uint8_t alpha = *pInputRGBA++;
+ alpha = alpha;
+ }
+
+ return planarData;
+}
+
+void RawImage::GenerateLayoutIndexes(const ILayoutTransform::Configuration& ltConfiguration) {
+ size_t nEntries = ltConfiguration._width * ltConfiguration._height * ltConfiguration._cVector;
+
+ uint32_t c_vector = ltConfiguration._cVector;
+ uint32_t width_stride = 1;
+ uint32_t height_stride = 1;
+ uint32_t input_width = ltConfiguration._width;
+ uint32_t input_height = ltConfiguration._height;
+ uint32_t input_channels = 3;
+ uint32_t output_width = ltConfiguration._width;
+ uint32_t output_width_banked = ltConfiguration._width;
+ uint32_t output_height = ltConfiguration._height;
+ uint32_t pad_left = 0;
+ uint32_t pad_top = 0;
+
+ _indexes.resize(nEntries, -1);
+
+ for (uint32_t c = 0; c < input_channels; c++) {
+ for (uint32_t h = 0; h < input_height; h++) {
+ for (uint32_t w = 0; w < input_width; w++) {
+ uint32_t output_w = (w + pad_left) / width_stride;
+ uint32_t output_h = (h + pad_top) / height_stride;
+ uint32_t output_d = c * height_stride * width_stride + ((h + pad_top) % height_stride) * width_stride +
+ (w + pad_left) % width_stride;
+ uint32_t output_d_c_vector = output_d / c_vector;
+ uint32_t cvec = output_d % c_vector;
+ uint32_t inIndex = c * input_height * input_width + h * input_width + w;
+
+ uint32_t outIndex = (output_d_c_vector * output_height * output_width_banked * c_vector) +
+ (output_h * output_width_banked * c_vector) + (output_w * c_vector) + cvec;
+
+ if ((output_h < output_height) && (output_w < output_width)) {
+ _indexes[outIndex] = static_cast<int32_t>(inIndex);
+ }
+ }
+ }
+ }
+}
diff --git a/python/openvino/runtime/streaming/image_streaming_app/raw_image.h b/python/openvino/runtime/streaming/image_streaming_app/raw_image.h
new file mode 100644
index 0000000..9cb08b4
--- /dev/null
+++ b/python/openvino/runtime/streaming/image_streaming_app/raw_image.h
@@ -0,0 +1,52 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#pragma once
+#include <cstdint>
+#include <filesystem>
+#include <memory>
+#include <vector>
+#include "ILayoutTransform.h"
+#include "bmp_file.h"
+#include "float16.h"
+
+class RawImage {
+ public:
+ RawImage(std::filesystem::path filePath,
+ bool runLayoutTransform,
+ const ILayoutTransform::Configuration& ltConfiguration);
+ uint8_t* GetData();
+ size_t GetSize();
+ std::string Filename() { return _filePath; }
+ bool DumpLayoutTransform();
+ static std::vector<uint16_t> LayoutTransform(uint32_t width,
+ uint32_t height,
+ const std::vector<uint8_t>& data,
+ const ILayoutTransform::Configuration& ltConfiguration);
+ static std::vector<uint8_t> MakePlanar(uint32_t width, uint32_t height, const std::vector<uint8_t>& data);
+ bool IsValid();
+
+ private:
+ static void GenerateLayoutIndexes(const ILayoutTransform::Configuration& ltConfiguration);
+ void LayoutTransform(const ILayoutTransform::Configuration& ltConfiguration);
+ static std::vector<uint16_t> LayoutTransform(const std::vector<uint8_t>& sourceData,
+ uint32_t numPixels,
+ const ILayoutTransform::Configuration& ltConfiguration);
+
+ std::filesystem::path _filePath;
+ std::shared_ptr<BmpFile> _spBmpFile;
+ std::vector<uint16_t> _layoutTransformData;
+ static std::vector<int32_t> _indexes;
+ bool _runLayoutTransform = false;
+ ILayoutTransform::Configuration _ltConfiguration;
+};
diff --git a/python/openvino/runtime/streaming/image_streaming_app/uio/CMakeLists.txt b/python/openvino/runtime/streaming/image_streaming_app/uio/CMakeLists.txt
new file mode 100644
index 0000000..4c445a2
--- /dev/null
+++ b/python/openvino/runtime/streaming/image_streaming_app/uio/CMakeLists.txt
@@ -0,0 +1,35 @@
+# Copyright 2023 Intel Corporation
+#
+# This software and the related documents are Intel copyrighted materials,
+# and your use of them is governed by the express license under which they
+# were provided to you ("License"). Unless the License provides otherwise,
+# you may not use, modify, copy, publish, distribute, disclose or transmit
+# this software or the related documents without Intel's prior written
+# permission.
+#
+# This software and the related documents are provided as is, with no express
+# or implied warranties, other than those that are expressly stated in the
+# License.
+
+project(uio)
+
+set(header_files "")
+set(source_files "")
+
+# specify the C++ standard
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+# Header files
+set(header_files ${header_files} "include/IUioDevice.h")
+set(header_files ${header_files} "source/UioDevice.h")
+
+# Source files
+set(source_files ${source_files} "source/UioDevice.cpp")
+
+set(all_files ${header_files} ${source_files})
+
+add_library(${PROJECT_NAME} STATIC ${all_files})
+
+# Include directories
+target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
diff --git a/python/openvino/runtime/streaming/image_streaming_app/uio/include/IUioDevice.h b/python/openvino/runtime/streaming/image_streaming_app/uio/include/IUioDevice.h
new file mode 100644
index 0000000..9b964a7
--- /dev/null
+++ b/python/openvino/runtime/streaming/image_streaming_app/uio/include/IUioDevice.h
@@ -0,0 +1,40 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#pragma once
+#include <filesystem>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace UIO {
+class DeviceItem {
+ public:
+ std::string _name;
+ uint32_t _index;
+ std::string _indexedName;
+ std::filesystem::path _rootPath;
+};
+
+class IDevice {
+ public:
+ static std::shared_ptr<IDevice> Load(const std::string& deviceName, uint32_t index = 0);
+ static std::vector<DeviceItem> GetDevices();
+
+ virtual uint32_t Read(uint32_t registerIndex) = 0;
+ virtual void Write(uint32_t registerIndex, uint32_t value) = 0;
+ virtual void ReadBlock(void* host_addr, size_t offset, size_t size) = 0;
+ virtual void WriteBlock(const void* host_addr, size_t offset, size_t size) = 0;
+};
+
+} // namespace UIO
diff --git a/python/openvino/runtime/streaming/image_streaming_app/uio/source/UioDevice.cpp b/python/openvino/runtime/streaming/image_streaming_app/uio/source/UioDevice.cpp
new file mode 100644
index 0000000..d1b5d17
--- /dev/null
+++ b/python/openvino/runtime/streaming/image_streaming_app/uio/source/UioDevice.cpp
@@ -0,0 +1,168 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#include "UioDevice.h"
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <filesystem>
+#include <fstream>
+
+namespace UIO {
+static const std::string uioDriverFolder = "/sys/class/uio";
+
+std::shared_ptr<IDevice> IDevice::Load(const std::string& deviceName, uint32_t index) {
+ std::vector<DeviceItem> deviceItems = GetDevices();
+ std::string indexedDeviceName = deviceName + std::to_string(index);
+
+ for (auto& deviceItem : deviceItems) {
+ if (deviceItem._indexedName == indexedDeviceName) {
+ auto spUioDevice = std::make_shared<Device>(deviceItem);
+ return spUioDevice->IsValid() ? spUioDevice : nullptr;
+ }
+ }
+
+ return nullptr;
+}
+
+std::vector<DeviceItem> IDevice::GetDevices() {
+ std::vector<DeviceItem> deviceItems;
+
+ for (const auto& entry : std::filesystem::directory_iterator(uioDriverFolder)) {
+ // Filter out uio*
+ if (entry.is_directory()) {
+ std::filesystem::path filePath = entry.path();
+ std::string stem = filePath.filename();
+ if (stem.substr(0, 3) == "uio") {
+ std::string indexedDeviceName = Device::ReadStringFromFile(filePath / "name");
+ if (not indexedDeviceName.empty()) {
+ std::string deviceName;
+ uint32_t index = 0;
+ Device::SplitIndexedDeviceName(indexedDeviceName, deviceName, index);
+ deviceItems.push_back({deviceName, index, indexedDeviceName, filePath});
+ }
+ }
+ }
+ }
+
+ return deviceItems;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+Device::Device(const DeviceItem& deviceItem) : _deviceItem(deviceItem) { MapRegion(); }
+
+Device::~Device() { UnmapRegion(); }
+
+bool Device::IsValid() { return (_fd >= 0); }
+
+uint32_t Device::Read(uint32_t registerIndex) {
+ if (registerIndex >= _maximumRegisterIndex) return 0;
+
+ uint32_t* pRegister = (uint32_t*)_pPtr;
+
+ uint32_t value = pRegister[registerIndex];
+ return value;
+}
+
+void Device::Write(uint32_t registerIndex, uint32_t value) {
+ if (registerIndex < _maximumRegisterIndex) {
+ uint32_t* pRegister = (uint32_t*)_pPtr;
+ pRegister[registerIndex] = value;
+ }
+}
+
+void Device::ReadBlock(void* pHostDestination, size_t offset, size_t size) {
+ if ((offset + size) < _size) {
+ uint8_t* pDeviceMem = (uint8_t*)_pPtr + offset;
+ ::memcpy(pHostDestination, pDeviceMem, size);
+ }
+}
+
+void Device::WriteBlock(const void* pHostSourceAddress, size_t offset, size_t size) {
+ if ((offset + size) < _size) {
+ uint8_t* pDeviceMem = (uint8_t*)_pPtr + offset;
+ ::memcpy(pDeviceMem, pHostSourceAddress, size);
+ }
+}
+
+uint64_t Device::ReadValueFromFile(const std::filesystem::path& path) {
+ std::string line = ReadStringFromFile(path);
+ int base = (line.substr(0, 2) == "0x") ? 16 : 10;
+ return std::stoull(line, nullptr, base);
+}
+
+std::string Device::ReadStringFromFile(const std::filesystem::path& path) {
+ std::ifstream inputStream(path);
+ if (inputStream.good()) {
+ std::string line;
+ std::getline(inputStream, line);
+ return line;
+ }
+ return "";
+}
+
+bool Device::MapRegion() {
+ _size = ReadValueFromFile(_deviceItem._rootPath / "maps/map0/size");
+ _offset = ReadValueFromFile(_deviceItem._rootPath / "maps/map0/offset");
+ _physicalAddress = ReadValueFromFile(_deviceItem._rootPath / "maps/map0/addr");
+ _maximumRegisterIndex = _size / sizeof(uint32_t);
+
+ std::filesystem::path uioDevicePath = "/dev";
+ std::filesystem::path uioDeviceId = _deviceItem._rootPath.stem();
+ uioDevicePath /= uioDeviceId;
+
+ _fd = ::open(uioDevicePath.c_str(), O_RDWR);
+ if (_fd < 0) {
+ return false;
+ }
+
+ // Map the region into userspace
+ _pBase = (uint8_t*)::mmap(NULL, (size_t)_size, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0);
+ if (_pBase == MAP_FAILED) {
+ return false;
+ }
+
+ // CST base address is at _pBase + _offset
+ _pPtr = (uint32_t*)(_pBase + _offset);
+
+ return true;
+};
+
+void Device::UnmapRegion() {
+ int r = 0;
+ if (_pBase) {
+ r = ::munmap(_pBase, _size);
+ _pBase = nullptr;
+ }
+
+ if (_fd >= 0) {
+ r = ::close(_fd);
+ _fd = -1;
+ }
+ (void)r;
+}
+
+void Device::SplitIndexedDeviceName(const std::string& indexedDeviceName, std::string& deviceName, uint32_t& index) {
+ int32_t len = static_cast<int32_t>(indexedDeviceName.length());
+ int32_t nDecimals = 0;
+ for (int32_t i = (len - 1); i >= 0; i--) {
+ if (::isdigit(indexedDeviceName[i])) nDecimals++;
+ }
+
+ deviceName = indexedDeviceName.substr(0, len - nDecimals);
+ index = std::stoul(indexedDeviceName.substr(len - nDecimals));
+}
+
+} // namespace UIO
diff --git a/python/openvino/runtime/streaming/image_streaming_app/uio/source/UioDevice.h b/python/openvino/runtime/streaming/image_streaming_app/uio/source/UioDevice.h
new file mode 100644
index 0000000..49c6f51
--- /dev/null
+++ b/python/openvino/runtime/streaming/image_streaming_app/uio/source/UioDevice.h
@@ -0,0 +1,56 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#pragma once
+
+#include <filesystem>
+#include <memory>
+#include <string>
+#include <vector>
+#include "IUioDevice.h"
+
+namespace UIO {
+class Device : public IDevice {
+ public:
+ Device(const DeviceItem& deviceItem);
+ ~Device();
+
+ // IDevice interface
+ uint32_t Read(uint32_t registerIndex) override;
+ void Write(uint32_t registerIndex, uint32_t value) override;
+ void ReadBlock(void* host_addr, size_t offset, size_t size) override;
+ void WriteBlock(const void* host_addr, size_t offset, size_t size) override;
+
+ bool IsValid();
+ static uint64_t ReadValueFromFile(const std::filesystem::path& path);
+ static std::string ReadStringFromFile(const std::filesystem::path& path);
+ static void SplitIndexedDeviceName(const std::string& indexedDeviceName, std::string& deviceName, uint32_t& index);
+
+ private:
+ Device() = delete;
+ Device(Device const&) = delete;
+ void operator=(Device const&) = delete;
+
+ bool MapRegion();
+ void UnmapRegion();
+
+ DeviceItem _deviceItem;
+ uint32_t _maximumRegisterIndex = 0;
+ int _fd = -1; // File pointer to UIO - Used to indicate the the Device is valid
+ uint64_t _physicalAddress = 0;
+ uint64_t _size = 0; // Size of the mmapped region
+ uint64_t _offset = 0; // Offset of the first register
+ uint8_t* _pBase = nullptr; // Base of the mmapped region
+ uint32_t* _pPtr = nullptr; // The first register
+};
+} // namespace UIO
diff --git a/python/openvino/runtime/streaming/runtime_scripts/run_image_stream.sh b/python/openvino/runtime/streaming/runtime_scripts/run_image_stream.sh
new file mode 100755
index 0000000..db63761
--- /dev/null
+++ b/python/openvino/runtime/streaming/runtime_scripts/run_image_stream.sh
@@ -0,0 +1,7 @@
+#! /bin/sh
+# This script should be run from the /home/root/app folder.
+
+# Run the image streaming app, specifying the folder containing the source
+# images, and an upload rate
+./image_streaming_app -images_folder=/home/root/resnet-50-tf/sample_images -rate=50
+
diff --git a/python/openvino/runtime/streaming/runtime_scripts/run_inference_stream.sh b/python/openvino/runtime/streaming/runtime_scripts/run_inference_stream.sh
new file mode 100755
index 0000000..3d14302
--- /dev/null
+++ b/python/openvino/runtime/streaming/runtime_scripts/run_inference_stream.sh
@@ -0,0 +1,14 @@
+#! /bin/sh
+# This script should be run from the /home/root/app folder.
+
+# Set the location of the shared libraries:
+export LD_LIBRARY_PATH=.
+
+# Immediately after startup, a Linux process rngd sometimes
+# runs at 100% CPU for a few minutes. This can be stopped
+# safely as there is no dependency on this
+killall -9 rngd >& /dev/null
+
+# Run the inference app, specifying the compiled model, the architecutre file and the CoreDLA device name
+# nb the model must be compiled with no folding. Use the option --ffolding-option=0 with the dlac compiler
+./streaming_inference_app -model=/home/root/resnet-50-tf/RN50_Performance_no_folding.bin -arch=/home/root/resnet-50-tf/A10_Performance.arch -device=HETERO:FPGA
diff --git a/python/openvino/runtime/streaming/streaming_inference_app/CMakeLists.txt b/python/openvino/runtime/streaming/streaming_inference_app/CMakeLists.txt
new file mode 100644
index 0000000..ec9cc4f
--- /dev/null
+++ b/python/openvino/runtime/streaming/streaming_inference_app/CMakeLists.txt
@@ -0,0 +1,29 @@
+# Copyright 2023 Intel Corporation
+#
+# This software and the related documents are Intel copyrighted materials,
+# and your use of them is governed by the express license under which they
+# were provided to you ("License"). Unless the License provides otherwise,
+# you may not use, modify, copy, publish, distribute, disclose or transmit
+# this software or the related documents without Intel's prior written
+# permission.
+#
+# This software and the related documents are provided as is, with no express
+# or implied warranties, other than those that are expressly stated in the
+# License.
+
+project(streaming_inference_app)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+set(all_files
+ streaming_inference_app.cpp
+ streaming_inference_app.h
+ command_line.cpp
+ command_line.h)
+
+# Targets
+add_executable(${PROJECT_NAME} ${all_files})
+
+target_link_libraries(${PROJECT_NAME} openvino::runtime)
+target_link_libraries(${PROJECT_NAME} coreDlaRuntimePlugin)
diff --git a/python/openvino/runtime/streaming/streaming_inference_app/categories.txt b/python/openvino/runtime/streaming/streaming_inference_app/categories.txt
new file mode 100644
index 0000000..d77b8ba
--- /dev/null
+++ b/python/openvino/runtime/streaming/streaming_inference_app/categories.txt
@@ -0,0 +1,1001 @@
+-
+class ID 0
+class ID 1
+class ID 2
+class ID 3
+class ID 4
+class ID 5
+class ID 6
+class ID 7
+class ID 8
+class ID 9
+class ID 10
+class ID 11
+class ID 12
+class ID 13
+class ID 14
+class ID 15
+class ID 16
+class ID 17
+class ID 18
+class ID 19
+class ID 20
+class ID 21
+class ID 22
+class ID 23
+class ID 24
+class ID 25
+class ID 26
+class ID 27
+class ID 28
+class ID 29
+class ID 30
+class ID 31
+class ID 32
+class ID 33
+class ID 34
+class ID 35
+class ID 36
+class ID 37
+class ID 38
+class ID 39
+class ID 40
+class ID 41
+class ID 42
+class ID 43
+class ID 44
+class ID 45
+class ID 46
+class ID 47
+class ID 48
+class ID 49
+class ID 50
+class ID 51
+class ID 52
+class ID 53
+class ID 54
+class ID 55
+class ID 56
+class ID 57
+class ID 58
+class ID 59
+class ID 60
+class ID 61
+class ID 62
+class ID 63
+class ID 64
+class ID 65
+class ID 66
+class ID 67
+class ID 68
+class ID 69
+class ID 70
+class ID 71
+class ID 72
+class ID 73
+class ID 74
+class ID 75
+class ID 76
+class ID 77
+class ID 78
+class ID 79
+class ID 80
+class ID 81
+class ID 82
+class ID 83
+class ID 84
+class ID 85
+class ID 86
+class ID 87
+class ID 88
+class ID 89
+class ID 90
+class ID 91
+class ID 92
+class ID 93
+class ID 94
+class ID 95
+class ID 96
+class ID 97
+class ID 98
+class ID 99
+class ID 100
+class ID 101
+class ID 102
+class ID 103
+class ID 104
+class ID 105
+class ID 106
+class ID 107
+class ID 108
+class ID 109
+class ID 110
+class ID 111
+class ID 112
+class ID 113
+class ID 114
+class ID 115
+class ID 116
+class ID 117
+class ID 118
+class ID 119
+class ID 120
+class ID 121
+class ID 122
+class ID 123
+class ID 124
+class ID 125
+class ID 126
+class ID 127
+class ID 128
+class ID 129
+class ID 130
+class ID 131
+class ID 132
+class ID 133
+class ID 134
+class ID 135
+class ID 136
+class ID 137
+class ID 138
+class ID 139
+class ID 140
+class ID 141
+class ID 142
+class ID 143
+class ID 144
+class ID 145
+class ID 146
+class ID 147
+class ID 148
+class ID 149
+class ID 150
+class ID 151
+class ID 152
+class ID 153
+class ID 154
+class ID 155
+class ID 156
+class ID 157
+class ID 158
+class ID 159
+class ID 160
+class ID 161
+class ID 162
+class ID 163
+class ID 164
+class ID 165
+class ID 166
+class ID 167
+class ID 168
+class ID 169
+class ID 170
+class ID 171
+class ID 172
+class ID 173
+class ID 174
+class ID 175
+class ID 176
+class ID 177
+class ID 178
+class ID 179
+class ID 180
+class ID 181
+class ID 182
+class ID 183
+class ID 184
+class ID 185
+class ID 186
+class ID 187
+class ID 188
+class ID 189
+class ID 190
+class ID 191
+class ID 192
+class ID 193
+class ID 194
+class ID 195
+class ID 196
+class ID 197
+class ID 198
+class ID 199
+class ID 200
+class ID 201
+class ID 202
+class ID 203
+class ID 204
+class ID 205
+class ID 206
+class ID 207
+class ID 208
+class ID 209
+class ID 210
+class ID 211
+class ID 212
+class ID 213
+class ID 214
+class ID 215
+class ID 216
+class ID 217
+class ID 218
+class ID 219
+class ID 220
+class ID 221
+class ID 222
+class ID 223
+class ID 224
+class ID 225
+class ID 226
+class ID 227
+class ID 228
+class ID 229
+class ID 230
+class ID 231
+class ID 232
+class ID 233
+class ID 234
+class ID 235
+class ID 236
+class ID 237
+class ID 238
+class ID 239
+class ID 240
+class ID 241
+class ID 242
+class ID 243
+class ID 244
+class ID 245
+class ID 246
+class ID 247
+class ID 248
+class ID 249
+class ID 250
+class ID 251
+class ID 252
+class ID 253
+class ID 254
+class ID 255
+class ID 256
+class ID 257
+class ID 258
+class ID 259
+class ID 260
+class ID 261
+class ID 262
+class ID 263
+class ID 264
+class ID 265
+class ID 266
+class ID 267
+class ID 268
+class ID 269
+class ID 270
+class ID 271
+class ID 272
+class ID 273
+class ID 274
+class ID 275
+class ID 276
+class ID 277
+class ID 278
+class ID 279
+class ID 280
+class ID 281
+class ID 282
+class ID 283
+class ID 284
+class ID 285
+class ID 286
+class ID 287
+class ID 288
+class ID 289
+class ID 290
+class ID 291
+class ID 292
+class ID 293
+class ID 294
+class ID 295
+class ID 296
+class ID 297
+class ID 298
+class ID 299
+class ID 300
+class ID 301
+class ID 302
+class ID 303
+class ID 304
+class ID 305
+class ID 306
+class ID 307
+class ID 308
+class ID 309
+class ID 310
+class ID 311
+class ID 312
+class ID 313
+class ID 314
+class ID 315
+class ID 316
+class ID 317
+class ID 318
+class ID 319
+class ID 320
+class ID 321
+class ID 322
+class ID 323
+class ID 324
+class ID 325
+class ID 326
+class ID 327
+class ID 328
+class ID 329
+class ID 330
+class ID 331
+class ID 332
+class ID 333
+class ID 334
+class ID 335
+class ID 336
+class ID 337
+class ID 338
+class ID 339
+class ID 340
+class ID 341
+class ID 342
+class ID 343
+class ID 344
+class ID 345
+class ID 346
+class ID 347
+class ID 348
+class ID 349
+class ID 350
+class ID 351
+class ID 352
+class ID 353
+class ID 354
+class ID 355
+class ID 356
+class ID 357
+class ID 358
+class ID 359
+class ID 360
+class ID 361
+class ID 362
+class ID 363
+class ID 364
+class ID 365
+class ID 366
+class ID 367
+class ID 368
+class ID 369
+class ID 370
+class ID 371
+class ID 372
+class ID 373
+class ID 374
+class ID 375
+class ID 376
+class ID 377
+class ID 378
+class ID 379
+class ID 380
+class ID 381
+class ID 382
+class ID 383
+class ID 384
+class ID 385
+class ID 386
+class ID 387
+class ID 388
+class ID 389
+class ID 390
+class ID 391
+class ID 392
+class ID 393
+class ID 394
+class ID 395
+class ID 396
+class ID 397
+class ID 398
+class ID 399
+class ID 400
+class ID 401
+class ID 402
+class ID 403
+class ID 404
+class ID 405
+class ID 406
+class ID 407
+class ID 408
+class ID 409
+class ID 410
+class ID 411
+class ID 412
+class ID 413
+class ID 414
+class ID 415
+class ID 416
+class ID 417
+class ID 418
+class ID 419
+class ID 420
+class ID 421
+class ID 422
+class ID 423
+class ID 424
+class ID 425
+class ID 426
+class ID 427
+class ID 428
+class ID 429
+class ID 430
+class ID 431
+class ID 432
+class ID 433
+class ID 434
+class ID 435
+class ID 436
+class ID 437
+class ID 438
+class ID 439
+class ID 440
+class ID 441
+class ID 442
+class ID 443
+class ID 444
+class ID 445
+class ID 446
+class ID 447
+class ID 448
+class ID 449
+class ID 450
+class ID 451
+class ID 452
+class ID 453
+class ID 454
+class ID 455
+class ID 456
+class ID 457
+class ID 458
+class ID 459
+class ID 460
+class ID 461
+class ID 462
+class ID 463
+class ID 464
+class ID 465
+class ID 466
+class ID 467
+class ID 468
+class ID 469
+class ID 470
+class ID 471
+class ID 472
+class ID 473
+class ID 474
+class ID 475
+class ID 476
+class ID 477
+class ID 478
+class ID 479
+class ID 480
+class ID 481
+class ID 482
+class ID 483
+class ID 484
+class ID 485
+class ID 486
+class ID 487
+class ID 488
+class ID 489
+class ID 490
+class ID 491
+class ID 492
+class ID 493
+class ID 494
+class ID 495
+class ID 496
+class ID 497
+class ID 498
+class ID 499
+class ID 500
+class ID 501
+class ID 502
+class ID 503
+class ID 504
+class ID 505
+class ID 506
+class ID 507
+class ID 508
+class ID 509
+class ID 510
+class ID 511
+class ID 512
+class ID 513
+class ID 514
+class ID 515
+class ID 516
+class ID 517
+class ID 518
+class ID 519
+class ID 520
+class ID 521
+class ID 522
+class ID 523
+class ID 524
+class ID 525
+class ID 526
+class ID 527
+class ID 528
+class ID 529
+class ID 530
+class ID 531
+class ID 532
+class ID 533
+class ID 534
+class ID 535
+class ID 536
+class ID 537
+class ID 538
+class ID 539
+class ID 540
+class ID 541
+class ID 542
+class ID 543
+class ID 544
+class ID 545
+class ID 546
+class ID 547
+class ID 548
+class ID 549
+class ID 550
+class ID 551
+class ID 552
+class ID 553
+class ID 554
+class ID 555
+class ID 556
+class ID 557
+class ID 558
+class ID 559
+class ID 560
+class ID 561
+class ID 562
+class ID 563
+class ID 564
+class ID 565
+class ID 566
+class ID 567
+class ID 568
+class ID 569
+class ID 570
+class ID 571
+class ID 572
+class ID 573
+class ID 574
+class ID 575
+class ID 576
+class ID 577
+class ID 578
+class ID 579
+class ID 580
+class ID 581
+class ID 582
+class ID 583
+class ID 584
+class ID 585
+class ID 586
+class ID 587
+class ID 588
+class ID 589
+class ID 590
+class ID 591
+class ID 592
+class ID 593
+class ID 594
+class ID 595
+class ID 596
+class ID 597
+class ID 598
+class ID 599
+class ID 600
+class ID 601
+class ID 602
+class ID 603
+class ID 604
+class ID 605
+class ID 606
+class ID 607
+class ID 608
+class ID 609
+class ID 610
+class ID 611
+class ID 612
+class ID 613
+class ID 614
+class ID 615
+class ID 616
+class ID 617
+class ID 618
+class ID 619
+class ID 620
+class ID 621
+class ID 622
+class ID 623
+class ID 624
+class ID 625
+class ID 626
+class ID 627
+class ID 628
+class ID 629
+class ID 630
+class ID 631
+class ID 632
+class ID 633
+class ID 634
+class ID 635
+class ID 636
+class ID 637
+class ID 638
+class ID 639
+class ID 640
+class ID 641
+class ID 642
+class ID 643
+class ID 644
+class ID 645
+class ID 646
+class ID 647
+class ID 648
+class ID 649
+class ID 650
+class ID 651
+class ID 652
+class ID 653
+class ID 654
+class ID 655
+class ID 656
+class ID 657
+class ID 658
+class ID 659
+class ID 660
+class ID 661
+class ID 662
+class ID 663
+class ID 664
+class ID 665
+class ID 666
+class ID 667
+class ID 668
+class ID 669
+class ID 670
+class ID 671
+class ID 672
+class ID 673
+class ID 674
+class ID 675
+class ID 676
+class ID 677
+class ID 678
+class ID 679
+class ID 680
+class ID 681
+class ID 682
+class ID 683
+class ID 684
+class ID 685
+class ID 686
+class ID 687
+class ID 688
+class ID 689
+class ID 690
+class ID 691
+class ID 692
+class ID 693
+class ID 694
+class ID 695
+class ID 696
+class ID 697
+class ID 698
+class ID 699
+class ID 700
+class ID 701
+class ID 702
+class ID 703
+class ID 704
+class ID 705
+class ID 706
+class ID 707
+class ID 708
+class ID 709
+class ID 710
+class ID 711
+class ID 712
+class ID 713
+class ID 714
+class ID 715
+class ID 716
+class ID 717
+class ID 718
+class ID 719
+class ID 720
+class ID 721
+class ID 722
+class ID 723
+class ID 724
+class ID 725
+class ID 726
+class ID 727
+class ID 728
+class ID 729
+class ID 730
+class ID 731
+class ID 732
+class ID 733
+class ID 734
+class ID 735
+class ID 736
+class ID 737
+class ID 738
+class ID 739
+class ID 740
+class ID 741
+class ID 742
+class ID 743
+class ID 744
+class ID 745
+class ID 746
+class ID 747
+class ID 748
+class ID 749
+class ID 750
+class ID 751
+class ID 752
+class ID 753
+class ID 754
+class ID 755
+class ID 756
+class ID 757
+class ID 758
+class ID 759
+class ID 760
+class ID 761
+class ID 762
+class ID 763
+class ID 764
+class ID 765
+class ID 766
+class ID 767
+class ID 768
+class ID 769
+class ID 770
+class ID 771
+class ID 772
+class ID 773
+class ID 774
+class ID 775
+class ID 776
+class ID 777
+class ID 778
+class ID 779
+class ID 780
+class ID 781
+class ID 782
+class ID 783
+class ID 784
+class ID 785
+class ID 786
+class ID 787
+class ID 788
+class ID 789
+class ID 790
+class ID 791
+class ID 792
+class ID 793
+class ID 794
+class ID 795
+class ID 796
+class ID 797
+class ID 798
+class ID 799
+class ID 800
+class ID 801
+class ID 802
+class ID 803
+class ID 804
+class ID 805
+class ID 806
+class ID 807
+class ID 808
+class ID 809
+class ID 810
+class ID 811
+class ID 812
+class ID 813
+class ID 814
+class ID 815
+class ID 816
+class ID 817
+class ID 818
+class ID 819
+class ID 820
+class ID 821
+class ID 822
+class ID 823
+class ID 824
+class ID 825
+class ID 826
+class ID 827
+class ID 828
+class ID 829
+class ID 830
+class ID 831
+class ID 832
+class ID 833
+class ID 834
+class ID 835
+class ID 836
+class ID 837
+class ID 838
+class ID 839
+class ID 840
+class ID 841
+class ID 842
+class ID 843
+class ID 844
+class ID 845
+class ID 846
+class ID 847
+class ID 848
+class ID 849
+class ID 850
+class ID 851
+class ID 852
+class ID 853
+class ID 854
+class ID 855
+class ID 856
+class ID 857
+class ID 858
+class ID 859
+class ID 860
+class ID 861
+class ID 862
+class ID 863
+class ID 864
+class ID 865
+class ID 866
+class ID 867
+class ID 868
+class ID 869
+class ID 870
+class ID 871
+class ID 872
+class ID 873
+class ID 874
+class ID 875
+class ID 876
+class ID 877
+class ID 878
+class ID 879
+class ID 880
+class ID 881
+class ID 882
+class ID 883
+class ID 884
+class ID 885
+class ID 886
+class ID 887
+class ID 888
+class ID 889
+class ID 890
+class ID 891
+class ID 892
+class ID 893
+class ID 894
+class ID 895
+class ID 896
+class ID 897
+class ID 898
+class ID 899
+class ID 900
+class ID 901
+class ID 902
+class ID 903
+class ID 904
+class ID 905
+class ID 906
+class ID 907
+class ID 908
+class ID 909
+class ID 910
+class ID 911
+class ID 912
+class ID 913
+class ID 914
+class ID 915
+class ID 916
+class ID 917
+class ID 918
+class ID 919
+class ID 920
+class ID 921
+class ID 922
+class ID 923
+class ID 924
+class ID 925
+class ID 926
+class ID 927
+class ID 928
+class ID 929
+class ID 930
+class ID 931
+class ID 932
+class ID 933
+class ID 934
+class ID 935
+class ID 936
+class ID 937
+class ID 938
+class ID 939
+class ID 940
+class ID 941
+class ID 942
+class ID 943
+class ID 944
+class ID 945
+class ID 946
+class ID 947
+class ID 948
+class ID 949
+class ID 950
+class ID 951
+class ID 952
+class ID 953
+class ID 954
+class ID 955
+class ID 956
+class ID 957
+class ID 958
+class ID 959
+class ID 960
+class ID 961
+class ID 962
+class ID 963
+class ID 964
+class ID 965
+class ID 966
+class ID 967
+class ID 968
+class ID 969
+class ID 970
+class ID 971
+class ID 972
+class ID 973
+class ID 974
+class ID 975
+class ID 976
+class ID 977
+class ID 978
+class ID 979
+class ID 980
+class ID 981
+class ID 982
+class ID 983
+class ID 984
+class ID 985
+class ID 986
+class ID 987
+class ID 988
+class ID 989
+class ID 990
+class ID 991
+class ID 992
+class ID 993
+class ID 994
+class ID 995
+class ID 996
+class ID 997
+class ID 998
+class ID 999
diff --git a/python/openvino/runtime/streaming/streaming_inference_app/command_line.cpp b/python/openvino/runtime/streaming/streaming_inference_app/command_line.cpp
new file mode 100644
index 0000000..794310b
--- /dev/null
+++ b/python/openvino/runtime/streaming/streaming_inference_app/command_line.cpp
@@ -0,0 +1,72 @@
+// Copyright 2021-2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#include "command_line.h"
+#include <algorithm>
+
+static void TrimString(std::string& trimString) {
+ trimString.erase(0, trimString.find_first_not_of(" \n\r\t"));
+ trimString.erase(trimString.find_last_not_of(" \n\r\t") + 1);
+}
+
+static void MakeLower(std::string& stringValue) {
+ std::transform(stringValue.begin(), stringValue.end(), stringValue.begin(), ::tolower);
+}
+
+// Program -option=value
+CommandLine::CommandLine(int argumentCount, char* argumentValues[]) {
+ if (argumentCount > 0) _executableName = argumentValues[0];
+
+ for (int i = 1; i < argumentCount; i++) {
+ std::string inputString(argumentValues[i]);
+ std::string nextChar = inputString.substr(0, 1);
+ if ((nextChar == "-") or (nextChar == "/")) {
+ inputString = inputString.substr(1);
+ size_t equals = inputString.find("=");
+ std::string option;
+ std::string value;
+
+ if (equals == std::string::npos) {
+ option = inputString;
+ } else {
+ option = inputString.substr(0, equals);
+ value = inputString.substr(equals + 1);
+ }
+
+ TrimString(option);
+ TrimString(value);
+ MakeLower(option);
+ _optionMap[option] = value;
+ }
+ }
+}
+
+std::string CommandLine::GetOptionValue(const char* optionName) {
+ auto i = _optionMap.find(optionName);
+ if (i != _optionMap.end())
+ return i->second;
+ else
+ return "";
+}
+
+bool CommandLine::HaveOption(const char* optionName) { return (_optionMap.find(optionName) != _optionMap.end()); }
+
+bool CommandLine::GetOption(const char* optionName, std::string& optionValue) {
+ auto i = _optionMap.find(optionName);
+ if (i == _optionMap.end()) return false;
+
+ optionValue = i->second;
+ return true;
+}
+
+size_t CommandLine::NumOptions() { return _optionMap.size(); }
diff --git a/python/openvino/runtime/streaming/streaming_inference_app/command_line.h b/python/openvino/runtime/streaming/streaming_inference_app/command_line.h
new file mode 100644
index 0000000..41b12f0
--- /dev/null
+++ b/python/openvino/runtime/streaming/streaming_inference_app/command_line.h
@@ -0,0 +1,31 @@
+// Copyright 2021-2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#pragma once
+#include <string>
+#include <unordered_map>
+
+class CommandLine {
+ public:
+ CommandLine(int argumentCount, char* argumentValues[]);
+
+ std::string GetOptionValue(const char* optionName);
+ bool GetOption(const char* optionName, std::string& optionValue);
+ bool HaveOption(const char* optionName);
+ std::string GetExecutableName() { return _executableName; }
+ size_t NumOptions();
+
+ private:
+ std::string _executableName;
+ std::unordered_map<std::string, std::string> _optionMap;
+};
diff --git a/python/openvino/runtime/streaming/streaming_inference_app/streaming_inference_app.cpp b/python/openvino/runtime/streaming/streaming_inference_app/streaming_inference_app.cpp
new file mode 100644
index 0000000..d0e1ed0
--- /dev/null
+++ b/python/openvino/runtime/streaming/streaming_inference_app/streaming_inference_app.cpp
@@ -0,0 +1,413 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#include "streaming_inference_app.h"
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+#include <algorithm>
+#include <filesystem>
+#include <fstream>
+#include <sstream>
+#include <thread>
+#include "dla_plugin_config.hpp"
+
+using namespace std::chrono_literals;
+
+std::ofstream StreamingInferenceApp::_resultsStream("results.txt");
+std::mutex StreamingInferenceApp::_signalMutex;
+std::condition_variable StreamingInferenceApp::_signalConditionVariable;
+std::chrono::time_point<std::chrono::system_clock> StreamingInferenceApp::_startTime;
+
+int main(int numParams, char* paramValues[]) {
+ StreamingInferenceApp app(numParams, paramValues);
+
+ try {
+ app.Run();
+ } catch (const std::exception& ex) {
+ std::cerr << ex.what() << '\n';
+ }
+ return 0;
+}
+
+StreamingInferenceApp::StreamingInferenceApp(int numParams, char* paramValues[])
+ : _commandLine(numParams, paramValues) {
+ OsStartup();
+ LoadClassNames();
+}
+
+StreamingInferenceApp::~StreamingInferenceApp() {
+ timespec waitTimeout = {};
+ if (_pCancelSemaphore) {
+ // Reset the cancel semaphore
+ int r = 0;
+ do {
+ r = ::sem_timedwait(_pCancelSemaphore, &waitTimeout);
+ } while (r == 0);
+ ::sem_close(_pCancelSemaphore);
+ }
+
+ if (_pReadyForImageStreamSemaphore) {
+ // Reset the ready semaphore
+ int r = 0;
+ do {
+ r = ::sem_timedwait(_pReadyForImageStreamSemaphore, &waitTimeout);
+ } while (r == 0);
+ ::sem_close(_pReadyForImageStreamSemaphore);
+ }
+}
+
+void StreamingInferenceApp::Run() {
+ std::filesystem::path pluginsFilename = "plugins.xml";
+
+ std::string deviceName;
+ std::string arch;
+ std::string model;
+
+ // Get the command line options for the model, arch file, and device
+ if (not _commandLine.GetOption("model", model) or not _commandLine.GetOption("arch", arch) or
+ not _commandLine.GetOption("device", deviceName)) {
+ return Usage();
+ }
+
+ std::filesystem::path architectureFilename = arch;
+ std::filesystem::path compiledModelFilename = model;
+
+ // Check that the provided files do in fact exist
+ if (not CheckFileExists(architectureFilename, "architecture") or not CheckFileExists(pluginsFilename, "plugins") or
+ not CheckFileExists(compiledModelFilename, "compiled model")) {
+ return;
+ }
+
+ InferenceEngine::Core inferenceEngine(pluginsFilename);
+
+ // Setup CoreDLA private configuration parameters
+ const std::map<std::string, std::string> configParameters;
+ inferenceEngine.SetConfig({{DLIAPlugin::properties::arch_path.name(), architectureFilename}}, "FPGA");
+
+ // If dropSourceBuffers is 0, no input buffers are dropped
+ // If dropSourceBuffers is 1, then 1 buffer is processed, 1 gets dropped
+ // If dropSourceBuffers is 2, then 1 buffer is processed, 2 get dropped, etc.
+ uint32_t dropSourceBuffers = 0;
+
+ inferenceEngine.SetConfig({{DLIAPlugin::properties::streaming_drop_source_buffers.name(), std::to_string(dropSourceBuffers)},
+ {DLIAPlugin::properties::external_streaming.name(), CONFIG_VALUE(YES)}},
+ "FPGA");
+
+ std::ifstream inputFile(compiledModelFilename, std::fstream::binary);
+ if (not inputFile) {
+ std::cout << "Failed to load compiled model file.\n";
+ return;
+ }
+
+ // Load the model to the device
+ InferenceEngine::ExecutableNetwork importedNetwork = inferenceEngine.ImportNetwork(inputFile, deviceName, {});
+
+ // The plugin defines the number of inferences requests required for streaming
+ uint32_t numStreamingInferenceRequests = importedNetwork.GetMetric(DLIAPlugin::properties::num_streaming_inference_requests.name()).as<uint32_t>();
+ const std::string cancelSemaphoreName = importedNetwork.GetMetric(DLIAPlugin::properties::cancel_semaphore_name.name()).as<std::string>();
+ _cancelSemaphoreName = cancelSemaphoreName;
+
+ for (uint32_t i = 0; i < numStreamingInferenceRequests; i++) {
+ auto spInferenceData = std::make_shared<SingleInferenceData>(this, importedNetwork, i);
+ _inferences.push_back(spInferenceData);
+ }
+
+ // Start the inference requests. Streaming inferences will reschedule
+ // themselves when complete
+ for (auto& inference : _inferences) {
+ inference->StartAsync();
+ }
+
+ std::cout << "Ready to start image input stream.\n";
+
+ // Signal the image streaming app that we are ready, so it can
+ // begin transferring files
+ SetReadyForImageStreamSemaphore();
+
+ // Wait until Ctrl+C
+ bool done = false;
+ while (not done) {
+ std::unique_lock<std::mutex> lock(_signalMutex);
+ done = (_signalConditionVariable.wait_for(lock, 1000ms) != std::cv_status::timeout);
+ }
+
+ SetShutdownSemaphore();
+
+ for (auto& inference : _inferences) {
+ inference->Cancel();
+ }
+
+ _inferences.clear();
+}
+
+
+void StreamingInferenceApp::SetShutdownSemaphore() {
+ _pCancelSemaphore = ::sem_open(_cancelSemaphoreName.c_str(), O_CREAT, 0644, 0);
+ if (_pCancelSemaphore) {
+ ::sem_post(_pCancelSemaphore);
+ }
+}
+
+
+void StreamingInferenceApp::SetReadyForImageStreamSemaphore() {
+ _pReadyForImageStreamSemaphore = ::sem_open("/CoreDLA_ready_for_streaming", O_CREAT, 0644, 0);
+ if (_pReadyForImageStreamSemaphore) {
+ ::sem_post(_pReadyForImageStreamSemaphore);
+ }
+}
+
+
+/**
+ * Print a help menu to the console
+ */
+void StreamingInferenceApp::Usage() {
+ std::cout << "Usage:\n";
+ std::cout << "\tstreaming_inference_app -model=<model> -arch=<arch> -device=<device>\n\n";
+ std::cout << "Where:\n";
+ std::cout << "\t<model> is the compiled model binary file, eg /home/root/resnet-50-tf/RN50_Performance_no_folding.bin\n";
+ std::cout << "\t<arch> is the architecture file, eg /home/root/resnet-50-tf/A10_Performance.arch\n";
+ std::cout << "\t<device> is the OpenVINO device ID, eg HETERO:FPGA or HETERO:FPGA,CPU\n";
+}
+
+
+/**
+ * Check that a file exists
+ *
+ * @param[in] filename Filename to check
+ * @param[in] message Description of file to display if it does not exist
+ * @returns true if the file exists, false otherwise
+ */
+bool StreamingInferenceApp::CheckFileExists(const std::filesystem::path& filename, const std::string& message) {
+ if (not std::filesystem::exists(filename)) {
+ std::cout << "Can't find " << message << ", '" << filename.c_str() << "'\n";
+ return false;
+ }
+
+ return true;
+}
+
+////////////
+
+std::atomic<uint32_t> SingleInferenceData::_atomic{0};
+uint32_t SingleInferenceData::_numResults = 0;
+
+SingleInferenceData::SingleInferenceData(StreamingInferenceApp* pApp,
+ InferenceEngine::ExecutableNetwork& importedNetwork,
+ uint32_t index)
+ : _pApp(pApp), _importedNetwork(importedNetwork), _index(index), _inferenceCount(0) {
+ // Set up output blob
+ InferenceEngine::ConstOutputsDataMap outputsInfo = importedNetwork.GetOutputsInfo();
+ std::shared_ptr<const InferenceEngine::Data> spOutputInfo = outputsInfo.begin()->second;
+ std::string outputName = outputsInfo.begin()->first;
+
+ _spOutputBlob = CreateOutputBlob(spOutputInfo);
+
+ // Create an inference request and set its completion callback
+ _inferenceRequest = importedNetwork.CreateInferRequest();
+ auto inferenceRequestCompleteCB = [=]() { ProcessResult(); };
+ _inferenceRequest.SetCompletionCallback(inferenceRequestCompleteCB);
+
+ // Assign the output blob to the inference request
+ _inferenceRequest.SetBlob(outputName, _spOutputBlob);
+}
+
+
+std::shared_ptr<InferenceEngine::Blob> SingleInferenceData::CreateOutputBlob(
+ std::shared_ptr<const InferenceEngine::Data> spOutputInfo) {
+ const InferenceEngine::TensorDesc& outputTensorDesc = spOutputInfo->getTensorDesc();
+ std::shared_ptr<InferenceEngine::Blob> pOutputBob = InferenceEngine::make_shared_blob<float>(outputTensorDesc);
+ pOutputBob->allocate();
+
+ InferenceEngine::MemoryBlob::Ptr pMemoryBlob = InferenceEngine::as<InferenceEngine::MemoryBlob>(pOutputBob);
+ if (pMemoryBlob) {
+ auto lockedMemory = pMemoryBlob->wmap();
+ float* pOutputBlobData = lockedMemory.as<float*>();
+ if (pOutputBlobData) {
+ size_t outputSize = pOutputBob->size();
+ for (size_t i = 0; i < outputSize; i++) {
+ pOutputBlobData[i] = 0.0f;
+ }
+ }
+ }
+
+ return pOutputBob;
+}
+
+
+void SingleInferenceData::StartAsync() {
+ _inferenceCount = _atomic++;
+ _inferenceRequest.StartAsync();
+}
+
+void SingleInferenceData::Wait() { _inferenceRequest.Wait(); }
+
+void SingleInferenceData::Cancel() { _inferenceRequest.Cancel(); }
+
+
+/**
+ * Stores the results of an inference
+ *
+ * The index corresponds to the category of the image, and the score is
+ * the confidence level of the image.
+ */
+class ResultItem {
+ public:
+ uint32_t _index;
+ float _score;
+ bool operator<(const ResultItem& other) { return (_score > other._score); }
+};
+
+
+/**
+ * Called when inference request has completed
+ *
+ * The inference results are floating point numbers consisting of the score for each category.
+ * The scores are then sorted and the highest is written to the console. The top 5 scores of the
+ * first 1000 images are saved to results.txt.
+ *
+ * Set as a callback in SingleInferenceData()
+ */
+void SingleInferenceData::ProcessResult() {
+ if (_pApp and _pApp->IsCancelling()) {
+ return;
+ }
+
+ // Increment the number of inference results that have returned thus far
+ _numResults++;
+
+ // If this is the first returned inference, store the current time to calculate the inference rate
+ if (_numResults == 1) {
+ StreamingInferenceApp::_startTime = std::chrono::system_clock::now();
+ } else if (_numResults == 101) {
+ // The inference rate is calculated afer 100 results have been received
+ auto endTime = std::chrono::system_clock::now();
+ auto duration = endTime - StreamingInferenceApp::_startTime;
+ double durationMS = (double)std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
+ double durationSecondsOne = durationMS / 100000.0;
+ double rate = 1.0 / durationSecondsOne;
+ std::cout << "Inference rate = " << rate << '\n';
+ }
+
+ // Create a float pointer to the returned data
+ size_t outputSize = _spOutputBlob->size();
+ float* pOutputData = _spOutputBlob->buffer().as<float*>();
+ if (!pOutputData) {
+ return;
+ }
+
+ // Store each score as a ResultItem
+ std::vector<ResultItem> results;
+ for (size_t i = 0; i < outputSize; i++) {
+ results.push_back({(uint32_t)i, pOutputData[i]});
+ }
+
+ // Sort the scores and set up the output streams
+ std::sort(results.begin(), results.end());
+ std::stringstream fileString;
+ std::stringstream outString;
+ bool flushFile = false;
+
+ // Store the top 5 results of the first 1000 images to be written to a file
+ if (_numResults <= 1000) {
+ fileString << "Result: image[" << _numResults << "]\n";
+ fileString << std::fixed << std::setprecision(1);
+
+ for (size_t i = 0; i < 5; i++) {
+ std::string className = _pApp->_imageNetClasses[results[i]._index];
+ float score = results[i]._score * 100.0f;
+ fileString << (i + 1) << ". " << className << ", score = " << score << '\n';
+ }
+
+ fileString << '\n';
+ }
+
+ if (_numResults == 1001) {
+ fileString << "End of results capture\n";
+ flushFile = true;
+ }
+
+ // Store the top score to write to the console
+ outString << std::fixed << std::setprecision(1);
+ std::string className = _pApp->_imageNetClasses[results[0]._index];
+ float score = results[0]._score * 100.0f;
+ outString << _numResults << " - " << className << ", score = " << score << '\n';
+
+ // Write the results to the file
+ std::string writeFileString = fileString.str();
+ if (not writeFileString.empty()) {
+ StreamingInferenceApp::_resultsStream << writeFileString;
+ if (flushFile) {
+ StreamingInferenceApp::_resultsStream << std::endl;
+ }
+ }
+
+ // Write the top score to the console
+ std::cout << outString.str();
+
+ // Start again
+ StartAsync();
+}
+
+
+/**
+ * Load the categories and store them in _imageNetClasses
+ */
+void StreamingInferenceApp::LoadClassNames() {
+ _imageNetClasses.resize(1001);
+
+ bool validClassFile = false;
+ std::filesystem::path classNameFilePath = "categories.txt";
+
+ if (std::filesystem::exists(classNameFilePath)) {
+ size_t classIndex = 0;
+ std::ifstream classNameStream(classNameFilePath);
+
+ if (classNameStream) {
+ std::string className;
+ while (std::getline(classNameStream, className)) {
+ if (classIndex < 1001) _imageNetClasses[classIndex] = className;
+
+ classIndex++;
+ }
+
+ validClassFile = (classIndex == 1001);
+ if (not validClassFile) {
+ std::cout << "Ignoring the categories.txt file. The file is expected to be a text file "
+ "with 1000 lines.\n";
+ }
+ }
+ } else {
+ std::cout << "No categories.txt file found. This file should contain 1000\n"
+ "lines, with the name of each category on each line.\n";
+ }
+
+ if (not validClassFile) {
+ _imageNetClasses[0] = "NONE";
+ for (size_t i = 1; i <= 1000; i++) {
+ _imageNetClasses[i] = "Image class #" + std::to_string(i);
+ }
+ }
+}
+
+static void SigIntHandler(int) {
+ std::cout << "\nCtrl+C detected. Shutting down application\n";
+ std::lock_guard<std::mutex> lock(StreamingInferenceApp::_signalMutex);
+ StreamingInferenceApp::_signalConditionVariable.notify_one();
+}
+
+void StreamingInferenceApp::OsStartup() {
+ // Ctrl+C will exit the application
+ signal(SIGINT, SigIntHandler);
+}
diff --git a/python/openvino/runtime/streaming/streaming_inference_app/streaming_inference_app.h b/python/openvino/runtime/streaming/streaming_inference_app/streaming_inference_app.h
new file mode 100644
index 0000000..3cdafa0
--- /dev/null
+++ b/python/openvino/runtime/streaming/streaming_inference_app/streaming_inference_app.h
@@ -0,0 +1,74 @@
+// Copyright 2023 Intel Corporation.
+//
+// This software and the related documents are Intel copyrighted materials,
+// and your use of them is governed by the express license under which they
+// were provided to you ("License"). Unless the License provides otherwise,
+// you may not use, modify, copy, publish, distribute, disclose or transmit
+// this software or the related documents without Intel's prior written
+// permission.
+//
+// This software and the related documents are provided as is, with no express
+// or implied warranties, other than those that are expressly stated in the
+// License.
+
+#pragma once
+#include <semaphore.h>
+#include <atomic>
+#include <condition_variable>
+#include <filesystem>
+#include "command_line.h"
+#include "inference_engine.hpp"
+
+class SingleInferenceData;
+using SingleInferenceDataPtr = std::shared_ptr<SingleInferenceData>;
+
+class StreamingInferenceApp {
+ friend class SingleInferenceData;
+
+ public:
+ StreamingInferenceApp(int numParams, char* paramValues[]);
+ ~StreamingInferenceApp();
+ void Usage();
+ void Run();
+ bool IsCancelling() { return (_pCancelSemaphore != nullptr); }
+
+ static std::mutex _signalMutex;
+ static std::condition_variable _signalConditionVariable;
+ static std::chrono::time_point<std::chrono::system_clock> _startTime;
+ static std::ofstream _resultsStream;
+
+ private:
+ void OsStartup();
+ bool CheckFileExists(const std::filesystem::path& filename, const std::string& message);
+ void SetShutdownSemaphore();
+ void SetReadyForImageStreamSemaphore();
+ void LoadClassNames();
+
+ std::vector<SingleInferenceDataPtr> _inferences;
+ CommandLine _commandLine;
+ sem_t* _pCancelSemaphore = nullptr;
+ sem_t* _pReadyForImageStreamSemaphore = nullptr;
+ std::string _cancelSemaphoreName;
+ std::vector<std::string> _imageNetClasses;
+};
+
+class SingleInferenceData {
+ public:
+ SingleInferenceData(StreamingInferenceApp* pApp, InferenceEngine::ExecutableNetwork& importedNetwork, uint32_t index);
+ void StartAsync();
+ void Wait();
+ void Cancel();
+
+ private:
+ void ProcessResult();
+ std::shared_ptr<InferenceEngine::Blob> CreateOutputBlob(std::shared_ptr<const InferenceEngine::Data> spOutputInfo);
+
+ StreamingInferenceApp* _pApp;
+ InferenceEngine::ExecutableNetwork& _importedNetwork;
+ std::shared_ptr<InferenceEngine::Blob> _spOutputBlob;
+ InferenceEngine::InferRequest _inferenceRequest;
+ uint32_t _index;
+ uint32_t _inferenceCount;
+ static uint32_t _numResults;
+ static std::atomic<uint32_t> _atomic;
+};