From 0d9e8fc2704c765a78c805c392586e57109363a7 Mon Sep 17 00:00:00 2001 From: MONCADA Victor Date: Mon, 4 Nov 2024 09:18:07 +0100 Subject: [PATCH 01/15] Added support for reading h264 videos from memory --- src/cpp/tools/ReadFileChunk.cpp | 53 ++++++++++++++++++++++++++++++++- src/cpp/tools/ReadFileChunk.h | 5 ++++ src/cpp/video_io/video_io.cpp | 37 +++++++++++++++++++++++ src/cpp/video_io/video_io.h | 12 ++++++++ 4 files changed, 106 insertions(+), 1 deletion(-) diff --git a/src/cpp/tools/ReadFileChunk.cpp b/src/cpp/tools/ReadFileChunk.cpp index 17505482..f11fe7d4 100644 --- a/src/cpp/tools/ReadFileChunk.cpp +++ b/src/cpp/tools/ReadFileChunk.cpp @@ -216,6 +216,7 @@ void *createFileReader(FileAccess access) return reader; } + void destroyFileReader(void *r) { if (!r) @@ -383,4 +384,54 @@ FileAccess createFileAccess(const char *filename, int64_t chunk_size) res.read = &readFileChunk; res.opaque = f; return res; -} \ No newline at end of file +} + + +#define MEM_BLOCK_CHUNK 4096 + + +struct MemBlock +{ + void * ptr; + int64_t fize; +}; + +void destroyOpaqueMemoryHandle(void *opaque) +{ + MemBlock *f = (MemBlock *)opaque; + delete f; +} +int64_t readMemoryChunk(void *opaque, int64_t chunk, uint8_t *buf) +{ + MemBlock *f = (MemBlock *)opaque; + int64_t pos = chunk * MEM_BLOCK_CHUNK; + int64_t size = MEM_BLOCK_CHUNK; + if(pos >= f->size) + size = 0; + else if(pos + size >= f->size) + size = f->size - pos; + memcpy(buf,f->ptr+pos,size); + return size; +} +void memoryInfos(void *opaque, int64_t *fileSize, int64_t *chunkCount, int64_t *chunkSize) +{ + MemBlock *f = (MemBlock *)opaque; + *fileSize = f->fsize; + *chunkCount = f->size / MEM_BLOCK_CHUNK + (f->size % MEM_BLOCK_CHUNK ? 1 : 0); + *chunkSize = MEM_BLOCK_CHUNK; +} + +FileAccess createMemoryAccess( void *data, int64_t size) +{ + MemBlock *f = new MemBlock(); + f->ptr = data; + f->size = size; + + FileAccess res; + res.destroy = &destroyOpaqueMemoryHandle; + res.infos = &memoryInfos; + res.read = &readMemoryChunk; + res.opaque = f; + return res; +} + diff --git a/src/cpp/tools/ReadFileChunk.h b/src/cpp/tools/ReadFileChunk.h index d0ecd751..6b63a140 100644 --- a/src/cpp/tools/ReadFileChunk.h +++ b/src/cpp/tools/ReadFileChunk.h @@ -93,6 +93,11 @@ Create and return a FileAccess from a local file */ TOOLS_EXPORT FileAccess createFileAccess(const char *filename, int64_t chunk_size = 4096); +/** +Create and return a FileAccess working on a memory chunk +*/ +TOOLS_EXPORT FileAccess createMemoryAccess( void *data, int64_t size); + /** Create a file reader object from a #FileAccess object. This file reader can be passed to the librir function #open_camera_file_reader(). diff --git a/src/cpp/video_io/video_io.cpp b/src/cpp/video_io/video_io.cpp index 4651ba4d..45b79871 100644 --- a/src/cpp/video_io/video_io.cpp +++ b/src/cpp/video_io/video_io.cpp @@ -104,6 +104,43 @@ int open_camera_file_reader(void *file_reader, int *file_format) } } +int open_camera_from_memory(void *ptr, int64_t size, int *file_format); +{ + if (file_format) + *file_format = 0; + + IRFileLoader *loader = new IRFileLoader(); + void *reader = createFileReader(createMemoryAccess(ptr,size)); + + if (loader->openFileReader(reader)) + { + if (file_format) + { + if (loader->isPCR()) + *file_format = FILE_FORMAT_PCR; + else if (loader->isBIN()) + *file_format = FILE_FORMAT_WEST; + else if (loader->isPCREncapsulated()) + *file_format = FILE_FORMAT_PCR_ENCAPSULATED; + else if (loader->isZCompressed()) + *file_format = FILE_FORMAT_ZSTD_COMPRESSED; + else if (loader->isHCC()) + *file_format = FILE_FORMAT_HCC; + else if (loader->isH264()) + *file_format = FILE_FORMAT_H264; + else + *file_format = FILE_FORMAT_OTHER; + } + return set_void_ptr(loader); + } + else + { + delete loader; + logError(("Unable to open camera file: wrong file format")); + return 0; + } +} + int close_camera(int cam) { void *camera = get_void_ptr(cam); diff --git a/src/cpp/video_io/video_io.h b/src/cpp/video_io/video_io.h index 8ccd4ac2..b6b6db61 100644 --- a/src/cpp/video_io/video_io.h +++ b/src/cpp/video_io/video_io.h @@ -44,6 +44,18 @@ extern "C" \sa createFileReader */ IO_EXPORT int open_camera_file_reader(void *file_reader, int *file_format); + + /** + Opens a camera video file from a memory reader (created with #createMemoryReader()) object and returns + the camera descriptor on success, NULL otherwise. + If \a file_format is not NULL, it will be set to the file type: FILE_FORMAT_PCR, FILE_FORMAT_WEST, + FILE_FORMAT_PCR_ENCAPSULATED, FILE_FORMAT_ZSTD_COMPRESSED, FILE_FORMAT_H264 or 0 on error. + The created camera object will take ownership of the file reader and will destroy it with #destroyMemoryReader() + on closing. + \sa createMemoryReader + */ + IO_EXPORT int open_camera_from_memory(void *ptr, int64_t size, int *file_format); + /** Closes a previously opened camera or video file. */ From 0382caebb84ab059ff68056474d78375c9eb535b Mon Sep 17 00:00:00 2001 From: MONCADA Victor Date: Mon, 4 Nov 2024 10:50:12 +0100 Subject: [PATCH 02/15] updated python wrapper --- src/cpp/tools/ReadFileChunk.cpp | 4 ++-- src/python/librir/video_io/rir_video_io.py | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/cpp/tools/ReadFileChunk.cpp b/src/cpp/tools/ReadFileChunk.cpp index f11fe7d4..e9ca9db2 100644 --- a/src/cpp/tools/ReadFileChunk.cpp +++ b/src/cpp/tools/ReadFileChunk.cpp @@ -393,7 +393,7 @@ FileAccess createFileAccess(const char *filename, int64_t chunk_size) struct MemBlock { void * ptr; - int64_t fize; + int64_t size; }; void destroyOpaqueMemoryHandle(void *opaque) @@ -416,7 +416,7 @@ int64_t readMemoryChunk(void *opaque, int64_t chunk, uint8_t *buf) void memoryInfos(void *opaque, int64_t *fileSize, int64_t *chunkCount, int64_t *chunkSize) { MemBlock *f = (MemBlock *)opaque; - *fileSize = f->fsize; + *fileSize = f->size; *chunkCount = f->size / MEM_BLOCK_CHUNK + (f->size % MEM_BLOCK_CHUNK ? 1 : 0); *chunkSize = MEM_BLOCK_CHUNK; } diff --git a/src/python/librir/video_io/rir_video_io.py b/src/python/librir/video_io/rir_video_io.py index bb94a8b0..b4593fd1 100644 --- a/src/python/librir/video_io/rir_video_io.py +++ b/src/python/librir/video_io/rir_video_io.py @@ -49,6 +49,27 @@ def open_camera_file(filename): raise RuntimeError("cannot read file " + filename) return res +def open_camera_memory(buffer): + """ + Open a video from a buffer. + Returns an integer value representing the camera. This value can be used by the + functions: + - close_camera + - get_camera_identifier + - get_camera_pulse + - get_image_count + - get_image_time + - get_image_size + - supported_calibrations + - load_image + """ + pulse = np.zeros(1, dtype="i") + res = _video_io.open_camera_from_memory( + ct.cast(ct.c_char_p(buffer),ct.c_void_p), len(buffer), pulse.ctypes.data_as(ct.POINTER(ct.c_int)) + ) + if res == 0: + raise RuntimeError("cannot read video from memory ") + return res def video_file_format(filename): """ From 73b64e8bb1c6a24b632dd4301be4e5ee2ce911c1 Mon Sep 17 00:00:00 2001 From: MONCADA Victor Date: Mon, 4 Nov 2024 10:50:59 +0100 Subject: [PATCH 03/15] updated python wrapper --- src/cpp/video_io/video_io.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/video_io/video_io.cpp b/src/cpp/video_io/video_io.cpp index 45b79871..8e9b78d4 100644 --- a/src/cpp/video_io/video_io.cpp +++ b/src/cpp/video_io/video_io.cpp @@ -104,7 +104,7 @@ int open_camera_file_reader(void *file_reader, int *file_format) } } -int open_camera_from_memory(void *ptr, int64_t size, int *file_format); +int open_camera_from_memory(void *ptr, int64_t size, int *file_format) { if (file_format) *file_format = 0; From bbaa44361a18038fbce09b41ca1454d47d9e575a Mon Sep 17 00:00:00 2001 From: MONCADA Victor Date: Mon, 4 Nov 2024 10:56:42 +0100 Subject: [PATCH 04/15] correction --- src/cpp/tools/ReadFileChunk.cpp | 2 +- src/cpp/tools/ReadFileChunk.h | 2 +- src/cpp/video_io/video_io.cpp | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cpp/tools/ReadFileChunk.cpp b/src/cpp/tools/ReadFileChunk.cpp index e9ca9db2..f3ee7625 100644 --- a/src/cpp/tools/ReadFileChunk.cpp +++ b/src/cpp/tools/ReadFileChunk.cpp @@ -410,7 +410,7 @@ int64_t readMemoryChunk(void *opaque, int64_t chunk, uint8_t *buf) size = 0; else if(pos + size >= f->size) size = f->size - pos; - memcpy(buf,f->ptr+pos,size); + memcpy(buf,(char*)f->ptr + pos,size); return size; } void memoryInfos(void *opaque, int64_t *fileSize, int64_t *chunkCount, int64_t *chunkSize) diff --git a/src/cpp/tools/ReadFileChunk.h b/src/cpp/tools/ReadFileChunk.h index 6b63a140..9ff6043b 100644 --- a/src/cpp/tools/ReadFileChunk.h +++ b/src/cpp/tools/ReadFileChunk.h @@ -97,7 +97,7 @@ TOOLS_EXPORT FileAccess createFileAccess(const char *filename, int64_t chunk_siz Create and return a FileAccess working on a memory chunk */ TOOLS_EXPORT FileAccess createMemoryAccess( void *data, int64_t size); - + /** Create a file reader object from a #FileAccess object. This file reader can be passed to the librir function #open_camera_file_reader(). diff --git a/src/cpp/video_io/video_io.cpp b/src/cpp/video_io/video_io.cpp index 8e9b78d4..b05bc311 100644 --- a/src/cpp/video_io/video_io.cpp +++ b/src/cpp/video_io/video_io.cpp @@ -8,6 +8,8 @@ extern "C" #include "IRFileLoader.h" #include "h264.h" #include "HCCLoader.h" +#include "ReadFileChunk.h" + using namespace rir; #include From 79262762ea9d2ab6f8474fbf681815c4851139dc Mon Sep 17 00:00:00 2001 From: MONCADA Victor Date: Mon, 4 Nov 2024 11:00:16 +0100 Subject: [PATCH 05/15] correction --- src/cpp/video_io/video_io.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/video_io/video_io.cpp b/src/cpp/video_io/video_io.cpp index b05bc311..cdd6194d 100644 --- a/src/cpp/video_io/video_io.cpp +++ b/src/cpp/video_io/video_io.cpp @@ -112,7 +112,7 @@ int open_camera_from_memory(void *ptr, int64_t size, int *file_format) *file_format = 0; IRFileLoader *loader = new IRFileLoader(); - void *reader = createFileReader(createMemoryAccess(ptr,size)); + void *reader = createFileReader(createMemoryAccess(ptr,size)); if (loader->openFileReader(reader)) { From 5f559632d0b4bf51c7ca59e12107452abcdb397d Mon Sep 17 00:00:00 2001 From: MONCADA Victor Date: Mon, 4 Nov 2024 11:27:04 +0100 Subject: [PATCH 06/15] Added support for reading h264 videos from memory --- src/cpp/tools/ReadFileChunk.cpp | 53 ++++++++++++++++++++++++++++++++- src/cpp/tools/ReadFileChunk.h | 5 ++++ src/cpp/video_io/video_io.cpp | 37 +++++++++++++++++++++++ src/cpp/video_io/video_io.h | 12 ++++++++ 4 files changed, 106 insertions(+), 1 deletion(-) diff --git a/src/cpp/tools/ReadFileChunk.cpp b/src/cpp/tools/ReadFileChunk.cpp index 17505482..f11fe7d4 100644 --- a/src/cpp/tools/ReadFileChunk.cpp +++ b/src/cpp/tools/ReadFileChunk.cpp @@ -216,6 +216,7 @@ void *createFileReader(FileAccess access) return reader; } + void destroyFileReader(void *r) { if (!r) @@ -383,4 +384,54 @@ FileAccess createFileAccess(const char *filename, int64_t chunk_size) res.read = &readFileChunk; res.opaque = f; return res; -} \ No newline at end of file +} + + +#define MEM_BLOCK_CHUNK 4096 + + +struct MemBlock +{ + void * ptr; + int64_t fize; +}; + +void destroyOpaqueMemoryHandle(void *opaque) +{ + MemBlock *f = (MemBlock *)opaque; + delete f; +} +int64_t readMemoryChunk(void *opaque, int64_t chunk, uint8_t *buf) +{ + MemBlock *f = (MemBlock *)opaque; + int64_t pos = chunk * MEM_BLOCK_CHUNK; + int64_t size = MEM_BLOCK_CHUNK; + if(pos >= f->size) + size = 0; + else if(pos + size >= f->size) + size = f->size - pos; + memcpy(buf,f->ptr+pos,size); + return size; +} +void memoryInfos(void *opaque, int64_t *fileSize, int64_t *chunkCount, int64_t *chunkSize) +{ + MemBlock *f = (MemBlock *)opaque; + *fileSize = f->fsize; + *chunkCount = f->size / MEM_BLOCK_CHUNK + (f->size % MEM_BLOCK_CHUNK ? 1 : 0); + *chunkSize = MEM_BLOCK_CHUNK; +} + +FileAccess createMemoryAccess( void *data, int64_t size) +{ + MemBlock *f = new MemBlock(); + f->ptr = data; + f->size = size; + + FileAccess res; + res.destroy = &destroyOpaqueMemoryHandle; + res.infos = &memoryInfos; + res.read = &readMemoryChunk; + res.opaque = f; + return res; +} + diff --git a/src/cpp/tools/ReadFileChunk.h b/src/cpp/tools/ReadFileChunk.h index d0ecd751..6b63a140 100644 --- a/src/cpp/tools/ReadFileChunk.h +++ b/src/cpp/tools/ReadFileChunk.h @@ -93,6 +93,11 @@ Create and return a FileAccess from a local file */ TOOLS_EXPORT FileAccess createFileAccess(const char *filename, int64_t chunk_size = 4096); +/** +Create and return a FileAccess working on a memory chunk +*/ +TOOLS_EXPORT FileAccess createMemoryAccess( void *data, int64_t size); + /** Create a file reader object from a #FileAccess object. This file reader can be passed to the librir function #open_camera_file_reader(). diff --git a/src/cpp/video_io/video_io.cpp b/src/cpp/video_io/video_io.cpp index 4651ba4d..45b79871 100644 --- a/src/cpp/video_io/video_io.cpp +++ b/src/cpp/video_io/video_io.cpp @@ -104,6 +104,43 @@ int open_camera_file_reader(void *file_reader, int *file_format) } } +int open_camera_from_memory(void *ptr, int64_t size, int *file_format); +{ + if (file_format) + *file_format = 0; + + IRFileLoader *loader = new IRFileLoader(); + void *reader = createFileReader(createMemoryAccess(ptr,size)); + + if (loader->openFileReader(reader)) + { + if (file_format) + { + if (loader->isPCR()) + *file_format = FILE_FORMAT_PCR; + else if (loader->isBIN()) + *file_format = FILE_FORMAT_WEST; + else if (loader->isPCREncapsulated()) + *file_format = FILE_FORMAT_PCR_ENCAPSULATED; + else if (loader->isZCompressed()) + *file_format = FILE_FORMAT_ZSTD_COMPRESSED; + else if (loader->isHCC()) + *file_format = FILE_FORMAT_HCC; + else if (loader->isH264()) + *file_format = FILE_FORMAT_H264; + else + *file_format = FILE_FORMAT_OTHER; + } + return set_void_ptr(loader); + } + else + { + delete loader; + logError(("Unable to open camera file: wrong file format")); + return 0; + } +} + int close_camera(int cam) { void *camera = get_void_ptr(cam); diff --git a/src/cpp/video_io/video_io.h b/src/cpp/video_io/video_io.h index 8ccd4ac2..b6b6db61 100644 --- a/src/cpp/video_io/video_io.h +++ b/src/cpp/video_io/video_io.h @@ -44,6 +44,18 @@ extern "C" \sa createFileReader */ IO_EXPORT int open_camera_file_reader(void *file_reader, int *file_format); + + /** + Opens a camera video file from a memory reader (created with #createMemoryReader()) object and returns + the camera descriptor on success, NULL otherwise. + If \a file_format is not NULL, it will be set to the file type: FILE_FORMAT_PCR, FILE_FORMAT_WEST, + FILE_FORMAT_PCR_ENCAPSULATED, FILE_FORMAT_ZSTD_COMPRESSED, FILE_FORMAT_H264 or 0 on error. + The created camera object will take ownership of the file reader and will destroy it with #destroyMemoryReader() + on closing. + \sa createMemoryReader + */ + IO_EXPORT int open_camera_from_memory(void *ptr, int64_t size, int *file_format); + /** Closes a previously opened camera or video file. */ From 0cadb16b4df799aed81acfa67aac0a07d175f720 Mon Sep 17 00:00:00 2001 From: MONCADA Victor Date: Mon, 4 Nov 2024 11:27:05 +0100 Subject: [PATCH 07/15] updated python wrapper --- src/cpp/tools/ReadFileChunk.cpp | 4 ++-- src/python/librir/video_io/rir_video_io.py | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/cpp/tools/ReadFileChunk.cpp b/src/cpp/tools/ReadFileChunk.cpp index f11fe7d4..e9ca9db2 100644 --- a/src/cpp/tools/ReadFileChunk.cpp +++ b/src/cpp/tools/ReadFileChunk.cpp @@ -393,7 +393,7 @@ FileAccess createFileAccess(const char *filename, int64_t chunk_size) struct MemBlock { void * ptr; - int64_t fize; + int64_t size; }; void destroyOpaqueMemoryHandle(void *opaque) @@ -416,7 +416,7 @@ int64_t readMemoryChunk(void *opaque, int64_t chunk, uint8_t *buf) void memoryInfos(void *opaque, int64_t *fileSize, int64_t *chunkCount, int64_t *chunkSize) { MemBlock *f = (MemBlock *)opaque; - *fileSize = f->fsize; + *fileSize = f->size; *chunkCount = f->size / MEM_BLOCK_CHUNK + (f->size % MEM_BLOCK_CHUNK ? 1 : 0); *chunkSize = MEM_BLOCK_CHUNK; } diff --git a/src/python/librir/video_io/rir_video_io.py b/src/python/librir/video_io/rir_video_io.py index bb94a8b0..b4593fd1 100644 --- a/src/python/librir/video_io/rir_video_io.py +++ b/src/python/librir/video_io/rir_video_io.py @@ -49,6 +49,27 @@ def open_camera_file(filename): raise RuntimeError("cannot read file " + filename) return res +def open_camera_memory(buffer): + """ + Open a video from a buffer. + Returns an integer value representing the camera. This value can be used by the + functions: + - close_camera + - get_camera_identifier + - get_camera_pulse + - get_image_count + - get_image_time + - get_image_size + - supported_calibrations + - load_image + """ + pulse = np.zeros(1, dtype="i") + res = _video_io.open_camera_from_memory( + ct.cast(ct.c_char_p(buffer),ct.c_void_p), len(buffer), pulse.ctypes.data_as(ct.POINTER(ct.c_int)) + ) + if res == 0: + raise RuntimeError("cannot read video from memory ") + return res def video_file_format(filename): """ From 1720b5b8e1f7fd31c8a936fe305ace3f8de7cb0d Mon Sep 17 00:00:00 2001 From: MONCADA Victor Date: Mon, 4 Nov 2024 11:27:05 +0100 Subject: [PATCH 08/15] updated python wrapper --- src/cpp/video_io/video_io.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/video_io/video_io.cpp b/src/cpp/video_io/video_io.cpp index 45b79871..8e9b78d4 100644 --- a/src/cpp/video_io/video_io.cpp +++ b/src/cpp/video_io/video_io.cpp @@ -104,7 +104,7 @@ int open_camera_file_reader(void *file_reader, int *file_format) } } -int open_camera_from_memory(void *ptr, int64_t size, int *file_format); +int open_camera_from_memory(void *ptr, int64_t size, int *file_format) { if (file_format) *file_format = 0; From be14d952ca691aea62bc64cc2842f661eb1cde1f Mon Sep 17 00:00:00 2001 From: MONCADA Victor Date: Mon, 4 Nov 2024 11:27:06 +0100 Subject: [PATCH 09/15] correction --- src/cpp/tools/ReadFileChunk.cpp | 2 +- src/cpp/tools/ReadFileChunk.h | 2 +- src/cpp/video_io/video_io.cpp | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cpp/tools/ReadFileChunk.cpp b/src/cpp/tools/ReadFileChunk.cpp index e9ca9db2..f3ee7625 100644 --- a/src/cpp/tools/ReadFileChunk.cpp +++ b/src/cpp/tools/ReadFileChunk.cpp @@ -410,7 +410,7 @@ int64_t readMemoryChunk(void *opaque, int64_t chunk, uint8_t *buf) size = 0; else if(pos + size >= f->size) size = f->size - pos; - memcpy(buf,f->ptr+pos,size); + memcpy(buf,(char*)f->ptr + pos,size); return size; } void memoryInfos(void *opaque, int64_t *fileSize, int64_t *chunkCount, int64_t *chunkSize) diff --git a/src/cpp/tools/ReadFileChunk.h b/src/cpp/tools/ReadFileChunk.h index 6b63a140..9ff6043b 100644 --- a/src/cpp/tools/ReadFileChunk.h +++ b/src/cpp/tools/ReadFileChunk.h @@ -97,7 +97,7 @@ TOOLS_EXPORT FileAccess createFileAccess(const char *filename, int64_t chunk_siz Create and return a FileAccess working on a memory chunk */ TOOLS_EXPORT FileAccess createMemoryAccess( void *data, int64_t size); - + /** Create a file reader object from a #FileAccess object. This file reader can be passed to the librir function #open_camera_file_reader(). diff --git a/src/cpp/video_io/video_io.cpp b/src/cpp/video_io/video_io.cpp index 8e9b78d4..b05bc311 100644 --- a/src/cpp/video_io/video_io.cpp +++ b/src/cpp/video_io/video_io.cpp @@ -8,6 +8,8 @@ extern "C" #include "IRFileLoader.h" #include "h264.h" #include "HCCLoader.h" +#include "ReadFileChunk.h" + using namespace rir; #include From 7528004b66420d68981784a3c76978c454f4ebe6 Mon Sep 17 00:00:00 2001 From: MONCADA Victor Date: Mon, 4 Nov 2024 11:27:06 +0100 Subject: [PATCH 10/15] correction --- src/cpp/video_io/video_io.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/video_io/video_io.cpp b/src/cpp/video_io/video_io.cpp index b05bc311..cdd6194d 100644 --- a/src/cpp/video_io/video_io.cpp +++ b/src/cpp/video_io/video_io.cpp @@ -112,7 +112,7 @@ int open_camera_from_memory(void *ptr, int64_t size, int *file_format) *file_format = 0; IRFileLoader *loader = new IRFileLoader(); - void *reader = createFileReader(createMemoryAccess(ptr,size)); + void *reader = createFileReader(createMemoryAccess(ptr,size)); if (loader->openFileReader(reader)) { From dcdb13e2642aa01e815720e177b183f87653e419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?DUBUS=20L=C3=A9o=20243615?= Date: Mon, 4 Nov 2024 12:54:07 +0100 Subject: [PATCH 11/15] test: :white_check_mark: add some tests and make it pass --- src/python/librir/tools/FileAttributes.py | 7 ++- src/python/librir/tools/rir_tools.py | 11 ++++ src/python/librir/video_io/IRMovie.py | 63 +++++++++++++++-------- tests/python/test_IRMovie.py | 17 ++++-- 4 files changed, 73 insertions(+), 25 deletions(-) diff --git a/src/python/librir/tools/FileAttributes.py b/src/python/librir/tools/FileAttributes.py index 0aedfa08..2937b11d 100644 --- a/src/python/librir/tools/FileAttributes.py +++ b/src/python/librir/tools/FileAttributes.py @@ -18,6 +18,7 @@ attrs_global_attribute_value, attrs_frame_attribute_name, attrs_frame_attribute_value, + attrs_read_file_reader, attrs_timestamps, attrs_set_times, attrs_set_frame_attributes, @@ -44,7 +45,11 @@ def __init__(self, filename): Create a FileAttributes object from a filename. """ self.handle = 0 - self.handle = attrs_open_file(filename) + self.handle = ( + attrs_read_file_reader(filename) + if (isinstance(filename, bytes) or (filename == "")) + else attrs_open_file(filename) + ) # read timestamps and attributes self._timestamps = attrs_timestamps(self.handle) diff --git a/src/python/librir/tools/rir_tools.py b/src/python/librir/tools/rir_tools.py index 535728b2..45589bdf 100644 --- a/src/python/librir/tools/rir_tools.py +++ b/src/python/librir/tools/rir_tools.py @@ -142,6 +142,17 @@ def blosc_decompress_zstd(src): return out[0:ret] +def attrs_read_file_reader(data: bytes): + """ + Open attribute file reader and returns a handle to it + """ + _tools.attrs_read_file_reader.argtypes = [ct.c_char_p] + tmp = _tools.attrs_read_file_reader(data) + if tmp < 0: + raise RuntimeError("An error occured while calling 'attrs_read_file_reader'") + return tmp + + def attrs_open_file(filename): """ Open attribute file and returns a handle to it diff --git a/src/python/librir/video_io/IRMovie.py b/src/python/librir/video_io/IRMovie.py index 13102207..3336b20f 100644 --- a/src/python/librir/video_io/IRMovie.py +++ b/src/python/librir/video_io/IRMovie.py @@ -36,6 +36,7 @@ get_image_time, load_image, open_camera_file, + open_camera_memory, set_emissivity, set_global_emissivity, support_emissivity, @@ -77,7 +78,7 @@ def from_filename(cls, filename): return IRMovie(handle) @classmethod - def from_bytes(cls, data: bytes, times: List[float] = None, cthreads: int = 8): + def from_bytes(cls, data: bytes): """_summary_ Args: @@ -87,20 +88,14 @@ def from_bytes(cls, data: bytes, times: List[float] = None, cthreads: int = 8): Returns: _type_: _description_ """ - with tempfile.NamedTemporaryFile("wb", delete=False) as f: - filename = Path(f.name) - f.write(data) - with cls.from_filename(filename) as _instance: - _instance.__tempfile__ = filename - dst = Path(filename).parent / (f"{filename.stem}.h264") - _instance.to_h264(dst, times=times, cthreads=cthreads) - - instance = cls.from_filename(dst) - return instance + handle = open_camera_memory(data) + return cls(handle) @classmethod - def from_numpy_array(cls, arr, attrs=None, times=None, cthreads: int = 8): + def from_numpy_array( + cls, arr: np.ndarray, attrs=None, times=None, cthreads: int = 8 + ): """ Create a IRMovie object via numpy arrays. It creates non-pulse indexed IRMovie object. @@ -117,7 +112,17 @@ def from_numpy_array(cls, arr, attrs=None, times=None, cthreads: int = 8): else: raise ValueError("mismatch array shape. Must be 2D or 3D") data = header.astype(np.uint32).tobytes() + arr.astype(np.uint16).tobytes() - instance = cls.from_bytes(data, times=times, cthreads=cthreads) + + with tempfile.NamedTemporaryFile("wb", delete=False) as f: + filename = Path(f.name) + f.write(data) + + with cls.from_filename(filename) as _instance: + _instance.__tempfile__ = filename + dst = Path(filename).parent / (f"{filename.stem}.h264") + _instance.to_h264(dst, times=times, cthreads=cthreads) + + instance = cls.from_filename(dst) if attrs is not None: instance.attributes = attrs @@ -143,7 +148,12 @@ def __init__(self, handle): self._timestamps = None self._camstatus = None self._frame_attributes_d = {} - self._file_attributes = FileAttributes(self.filename) + + self._file_attributes = ( + FileAttributes(self.filename) + if self.filename is not None + else FileAttributes(self.handle) + ) self._file_attributes.attributes = get_global_attributes(self.handle) self._registration_file = None @@ -323,7 +333,8 @@ def bad_pixels_correction(self, value): @property def filename(self): - return Path(get_filename(self.handle)) # local filename + _f = get_filename(self.handle) + return Path(_f) if _f else None # local filename @property def calibration_files(self) -> List[str]: @@ -350,6 +361,9 @@ def attributes(self, value): @property def is_file_uncompressed(self): + if self.filename is None: + return False + statinfo = os.stat(self.filename) filesize = statinfo.st_size theoritical_uncompressed = ( @@ -479,10 +493,13 @@ def __len__(self): @property def timestamps(self): if self._timestamps is None: - self._timestamps = np.array( - [get_image_time(self.handle, i) * 1e-9 for i in range(self.images)], - dtype=np.float64, - ) + try: + self._timestamps = np.array( + [get_image_time(self.handle, i) * 1e-9 for i in range(self.images)], + dtype=np.float64, + ) + except RuntimeError as e: + logger.warning(f"No timestamps in {self}") return self._timestamps @timestamps.setter @@ -605,7 +622,11 @@ def to_h264( h, w = self.image_size if times is None: - times = list(self.timestamps) + times = ( + list(t * 1e9 for t in self.timestamps) + if self.timestamps is not None + else range(start_img, start_img + count) + ) with IRSaver(dst_filename, w, h, h, clevel) as s: s.set_global_attributes(attrs) s.set_parameter("threads", cthreads) @@ -619,7 +640,7 @@ def to_h264( else frame_attributes[saved] ) - s.add_image(img, times[i] * 1e9, attributes=_frame_attributes) + s.add_image(img, times[i], attributes=_frame_attributes) saved += 1 if saved % 100 == 0: diff --git a/tests/python/test_IRMovie.py b/tests/python/test_IRMovie.py index 0954eff2..09294fe6 100644 --- a/tests/python/test_IRMovie.py +++ b/tests/python/test_IRMovie.py @@ -136,9 +136,9 @@ def test_from_filename(filename): assert mov.filename == filename -def test_from_bytes_with_timestamps(movie_as_bytes, timestamps): - mov2 = IRMovie.from_bytes(movie_as_bytes, times=timestamps) - npt.assert_array_equal(mov2.timestamps, timestamps[: mov2.images]) +# def test_from_bytes_with_timestamps(movie_as_bytes, timestamps): +# mov2 = IRMovie.from_bytes(movie_as_bytes) +# npt.assert_array_equal(mov2.timestamps, timestamps[: mov2.images]) @pytest.mark.io @@ -248,3 +248,14 @@ def test_save_subset_of_movie_to_h264(movie: IRMovie, attrs: Dict[str, int]): assert len(subset_movie.frames_attributes) == subset_movie.images # dest_filename.unlink() + + +def test_ir_movie_from_buffer(filename: Path): + data = filename.read_bytes() + original_movie = IRMovie.from_filename(filename) + + with IRMovie.from_bytes(data) as mov: + npt.assert_array_equal(mov.data, original_movie.data) + assert mov.times == original_movie.times + assert mov.frames_attributes.compare(original_movie.frames_attributes).empty + assert mov.attributes == original_movie.attributes From 01ec70ca23fab45511b91d4915bec9562c9720a6 Mon Sep 17 00:00:00 2001 From: MONCADA Victor Date: Thu, 14 Nov 2024 09:56:04 +0100 Subject: [PATCH 12/15] Added support for reading in-memory file attributes --- src/cpp/tools/tools.cpp | 13 +++++++++++++ src/cpp/tools/tools.h | 7 +++++++ src/python/librir/tools/rir_tools.py | 12 ++++++++++++ 3 files changed, 32 insertions(+) diff --git a/src/cpp/tools/tools.cpp b/src/cpp/tools/tools.cpp index 0dec67b6..b83ab684 100644 --- a/src/cpp/tools/tools.cpp +++ b/src/cpp/tools/tools.cpp @@ -14,6 +14,8 @@ extern "C" #include "tools.h" #include "Log.h" #include "FileAttributes.h" +#include "ReadFileChunk.h" + using namespace rir; @@ -225,6 +227,17 @@ int attrs_read_file_reader(void *file_reader) } return set_void_ptr(attrs); } + +int attrs_open_from_memory(void *ptr, int64_t size) +{ + void *reader = createFileReader(createMemoryAccess(ptr,size)); + int res = attrs_read_file_reader(reader); + destroyFileReader(reader); + return res; +} + + + int attrs_open_file(const char *filename) { FileAttributes *attrs = new FileAttributes(); diff --git a/src/cpp/tools/tools.h b/src/cpp/tools/tools.h index 8d0b0658..e68e07f9 100644 --- a/src/cpp/tools/tools.h +++ b/src/cpp/tools/tools.h @@ -94,6 +94,13 @@ Log levels Returns 0 on error. */ TOOLS_EXPORT int attrs_read_file_reader(void *file_reader); + + /** + Read file attributes from an in-memory file. + This is a read-only version. + */ + TOOLS_EXPORT int attrs_open_from_memory(void *ptr, int64_t size); + /** Read file attributes. Returns the file attribute object handle. diff --git a/src/python/librir/tools/rir_tools.py b/src/python/librir/tools/rir_tools.py index 45589bdf..32964276 100644 --- a/src/python/librir/tools/rir_tools.py +++ b/src/python/librir/tools/rir_tools.py @@ -163,6 +163,18 @@ def attrs_open_file(filename): raise RuntimeError("An error occured while calling 'attrs_open_file'") return tmp +def attrs_open_buffer(buf : bytes): + """ + Open attributes from an in-memory file. + Attributes are read-only. + """ + res = _tools.attrs_open_from_memory( + ct.cast(ct.c_char_p(buffer),ct.c_void_p), len(buffer)) + ) + if res == 0: + raise RuntimeError("cannot read attributes from memory") + return res + def attrs_close(handle): """ From 68b6ebb8206f09cf9a55b8f7a7aca2e82c447006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?DUBUS=20L=C3=A9o=20243615?= Date: Thu, 14 Nov 2024 16:24:10 +0100 Subject: [PATCH 13/15] refactor: :recycle: remove unnecessary access to uncompressed file --- src/python/librir/video_io/IRMovie.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/python/librir/video_io/IRMovie.py b/src/python/librir/video_io/IRMovie.py index 3336b20f..bfb2e103 100644 --- a/src/python/librir/video_io/IRMovie.py +++ b/src/python/librir/video_io/IRMovie.py @@ -385,26 +385,10 @@ def height(self): def data(self) -> np.ndarray: """ Accessor to data contained in movie file. - There are two strategies for getting the data. - - - Movie filename is uncompressed (.pcr format): - data is not read through librir C functions. - Instead, the whole data is read directly from the file to speed up - computation. - It can only work with calibration_index == 0 - - - Movie filename is compressed (.bin, .h264 formats): - Every image must be read individually and stacked up @return: 3D np.array representing IR data movie """ - if self.is_file_uncompressed and (self._calibration_index == 0): - with open(self.filename, "rb") as f: - f.seek(self._header_offset) - data = np.fromfile(f, np.uint16, -1) - return np.reshape(data, (self.images,) + self.image_size) - else: - return self[:] + return self[:] @property def support_emissivity(self): From dc28643fa6e7b93e410e471bcafa800e9a28ea0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?DUBUS=20L=C3=A9o=20243615?= Date: Fri, 15 Nov 2024 13:29:16 +0100 Subject: [PATCH 14/15] fix: :recycle: adapt python wrapper to cpp interface --- src/python/librir/tools/FileAttributes.py | 25 ++++++++++++++--------- src/python/librir/tools/rir_tools.py | 23 +++++++++++---------- src/python/librir/video_io/IRMovie.py | 19 +++++++++-------- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/src/python/librir/tools/FileAttributes.py b/src/python/librir/tools/FileAttributes.py index 2937b11d..2a12123e 100644 --- a/src/python/librir/tools/FileAttributes.py +++ b/src/python/librir/tools/FileAttributes.py @@ -5,11 +5,11 @@ @author: VM213788 """ +from os import PathLike import numpy as np -from .. import low_level from ..low_level.misc import * from .rir_tools import ( - attrs_open_file, + attrs_open_buffer, attrs_close, attrs_discard, attrs_global_attribute_count, @@ -18,7 +18,7 @@ attrs_global_attribute_value, attrs_frame_attribute_name, attrs_frame_attribute_value, - attrs_read_file_reader, + attrs_open_file, attrs_timestamps, attrs_set_times, attrs_set_frame_attributes, @@ -39,17 +39,22 @@ class FileAttributes(object): Use the discard() function to avoid writting attributes to the file. """ - def __init__(self, filename): + @classmethod + def from_buffer(cls, buffer: bytes): + handle = attrs_open_buffer(buffer) + return cls(handle) + + @classmethod + def from_filename(cls, filename: PathLike): + handle = attrs_open_file(filename) + return cls(handle) + + def __init__(self, handle): """ Constructor. Create a FileAttributes object from a filename. """ - self.handle = 0 - self.handle = ( - attrs_read_file_reader(filename) - if (isinstance(filename, bytes) or (filename == "")) - else attrs_open_file(filename) - ) + self.handle = handle # read timestamps and attributes self._timestamps = attrs_timestamps(self.handle) diff --git a/src/python/librir/tools/rir_tools.py b/src/python/librir/tools/rir_tools.py index 32964276..68f13644 100644 --- a/src/python/librir/tools/rir_tools.py +++ b/src/python/librir/tools/rir_tools.py @@ -163,13 +163,14 @@ def attrs_open_file(filename): raise RuntimeError("An error occured while calling 'attrs_open_file'") return tmp -def attrs_open_buffer(buf : bytes): + +def attrs_open_buffer(buf: bytes): """ Open attributes from an in-memory file. Attributes are read-only. """ res = _tools.attrs_open_from_memory( - ct.cast(ct.c_char_p(buffer),ct.c_void_p), len(buffer)) + ct.cast(ct.c_char_p(buf), ct.c_void_p), len(buf) ) if res == 0: raise RuntimeError("cannot read attributes from memory") @@ -378,7 +379,7 @@ def attrs_timestamps(handle): def attrs_set_times(handle, times): _tools.attrs_set_times.argtypes = [ct.c_int, ct.POINTER(ct.c_int64), ct.c_int] - if type(times) != np.ndarray or times.dtype != np.int64: + if isinstance(times, np.ndarray) or (times.dtype != np.int64): times = np.array(list(times), dtype=np.int64) tmp = _tools.attrs_set_times( handle, times.ctypes.data_as(ct.POINTER(ct.c_int64)), int(times.shape[0]) @@ -415,15 +416,15 @@ def attrs_set_frame_attributes(handle, frame, attributes): klens = [] vlens = [] for k, v in attributes.items(): - if type(k) == bytes: + if isinstance(k, bytes): ks = k - elif type(k) == str: + elif isinstance(k, str): ks = k.encode("ascii") else: ks = str(k).encode("ascii") - if type(v) == bytes: + if isinstance(v, bytes): vs = v - elif type(v) == str: + elif isinstance(v, str): vs = v.encode("ascii") else: vs = str(v).encode("ascii") @@ -471,15 +472,15 @@ def attrs_set_global_attributes(handle, attributes): klens = [] vlens = [] for k, v in attributes.items(): - if type(k) == bytes: + if isinstance(k, bytes): ks = k - elif type(k) == str: + elif isinstance(k, str): ks = k.encode("utf8") else: ks = str(k).encode("utf8") - if type(v) == bytes: + if isinstance(v, bytes): vs = v - elif type(v) == str: + elif isinstance(v, str): vs = v.encode("utf8") else: vs = str(v).encode("utf8") diff --git a/src/python/librir/video_io/IRMovie.py b/src/python/librir/video_io/IRMovie.py index bfb2e103..305f48f7 100644 --- a/src/python/librir/video_io/IRMovie.py +++ b/src/python/librir/video_io/IRMovie.py @@ -67,6 +67,7 @@ def create_pcr_header(rows, columns, frequency=50, bits=16): class IRMovie(object): _header_offset = 1024 __tempfile__ = None + _file_attributes: Union[FileAttributes, None] = None handle = -1 _calibration_nickname_mapper = {"DL": "Digital Level"} _th = None @@ -75,7 +76,10 @@ class IRMovie(object): def from_filename(cls, filename): """Create an IRMovie object from a local filename""" handle = open_camera_file(str(filename)) - return IRMovie(handle) + instance = IRMovie(handle) + instance._file_attributes = FileAttributes.from_filename(filename) + instance._file_attributes.attributes = get_global_attributes(instance.handle) + return instance @classmethod def from_bytes(cls, data: bytes): @@ -90,7 +94,11 @@ def from_bytes(cls, data: bytes): """ handle = open_camera_memory(data) - return cls(handle) + instance = cls(handle) + instance._file_attributes = FileAttributes.from_buffer(data) + instance._file_attributes.attributes = get_global_attributes(instance.handle) + + return instance @classmethod def from_numpy_array( @@ -148,13 +156,6 @@ def __init__(self, handle): self._timestamps = None self._camstatus = None self._frame_attributes_d = {} - - self._file_attributes = ( - FileAttributes(self.filename) - if self.filename is not None - else FileAttributes(self.handle) - ) - self._file_attributes.attributes = get_global_attributes(self.handle) self._registration_file = None self.calibration = "DL" From 36096dd7565dc93d940ae47b9fd834df793dc937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?DUBUS=20L=C3=A9o=20243615?= Date: Mon, 18 Nov 2024 16:59:56 +0100 Subject: [PATCH 15/15] test: :white_check_mark: passing file_attributes tests --- src/python/librir/tools/rir_tools.py | 3 ++- tests/python/test_rir.py | 35 ++++++++++++++-------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/python/librir/tools/rir_tools.py b/src/python/librir/tools/rir_tools.py index 68f13644..1820f7b0 100644 --- a/src/python/librir/tools/rir_tools.py +++ b/src/python/librir/tools/rir_tools.py @@ -379,7 +379,8 @@ def attrs_timestamps(handle): def attrs_set_times(handle, times): _tools.attrs_set_times.argtypes = [ct.c_int, ct.POINTER(ct.c_int64), ct.c_int] - if isinstance(times, np.ndarray) or (times.dtype != np.int64): + + if not isinstance(times, np.ndarray) or (times.dtype != np.int64): times = np.array(list(times), dtype=np.int64) tmp = _tools.attrs_set_times( handle, times.ctypes.data_as(ct.POINTER(ct.c_int64)), int(times.shape[0]) diff --git a/tests/python/test_rir.py b/tests/python/test_rir.py index 43406349..29a4a30f 100644 --- a/tests/python/test_rir.py +++ b/tests/python/test_rir.py @@ -46,18 +46,21 @@ def test_blosc_decompress_zstd(compressed_blosc): print(rts.blosc_decompress_zstd(compressed_blosc)) -def test_file_attributes(): - attrs = fa.FileAttributes("attrs.test") +def test_file_attributes(movie: IRMovie): + filename = movie.filename + attrs = fa.FileAttributes.from_filename(filename) attrs.attributes = {"toto": 2, "tutu": "tata"} - attrs.timestamps = range(11) - attrs.set_frame_attributes(10, {"toto": 2, "tutu": "tata"}) + attrs.timestamps = range(movie.images) + attrs.set_frame_attributes(movie.images - 1, {"toto": 2, "tutu": "tata"}) attrs.close() - attrs = fa.FileAttributes("attrs.test") - print(attrs.attributes) - print(attrs.frame_count()) - print(attrs.frame_attributes(0)) - print(attrs.frame_attributes(10)) + attrs = fa.FileAttributes.from_filename(filename) + assert attrs.attributes == {"toto": b"2", "tutu": b"tata"} + assert attrs.frame_count() == movie.images + assert attrs.frame_attributes(movie.images - 1) == {"toto": b"2", "tutu": b"tata"} + attrs.close() + + # os.unlink(filename) polygon = [[0, 0], [1, 0], [1.2, 0.2], [1.8, 0.1], [5, 5], [3.2, 5], [0, 9]] @@ -97,8 +100,8 @@ def test_minimum_area_bbox(): assert ge.minimum_area_bbox(polygon) != ([np.nan, np.nan], 0.0, 0.0, np.nan, np.nan) -def test_translate(): - global img +def test_translate(img): + # global img img = np.ones((12, 12), dtype=np.float64) img = sp.translate(img, 1.2, 1.3, "constant", 0) print(img) @@ -107,8 +110,8 @@ def test_translate(): _img = sp.translate(_img, 1.2, 1.3, "constant", 0) -def test_gaussian_filter(): - global img +def test_gaussian_filter(img): + # global img img = sp.gaussian_filter(img, 0.75) print(img) @@ -142,16 +145,14 @@ def img(): return img - - -def test_bad_pixels(): +def test_bad_pixels(img): img = np.ones((20, 12), dtype=np.uint16) b = bp.BadPixels(img) print(b.correct(img)) del b -def test_label_image(): +def test_label_image(img): polygon = [[0, 0], [1, 0], [1.2, 0.2], [1.8, 0.1], [5, 5], [3.2, 5], [0, 9]] polygon2 = [[15, 15], [16, 17], [14, 17]] img = np.zeros((20, 20), np.int32)