Skip to content

Commit edad8a4

Browse files
committed
[#1374] Writing output img when using filename
1 parent 68ab2b7 commit edad8a4

4 files changed

Lines changed: 126 additions & 45 deletions

File tree

docs/source/Support/bskKnownIssues.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ Version |release|
1818
``Vizard image request acknowledgement was not received`` error is fixed in the current version.
1919
- The camera module now passes PNG encoding options as OpenCV key-value pairs, fixing OpNav image
2020
processing failures with OpenCV 4.13.
21+
- The camera module filename input was only used to test filters and never published to imageOut. This is fixed
22+
so images loaded from filename now follow the same processing and publishing pipeline as images from imageInMsg.
2123
- The MuJoCo dynamics wrapper now uses MuJoCo 3.7 element-name APIs, fixing builds after the MuJoCo
2224
3.7 upgrade.
2325
- SWIG 4.4.0 caused Basilisk build failures in some Python 3.13+ source-build configurations.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Fixed camera module to properly process and publish images loaded from the filename parameter, ensuring they follow the same processing pipeline as images from imageInMsg.

src/simulation/sensors/camera/_UnitTest/test_camera.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,90 @@
5757
# @pytest.mark.xfail(conditionstring)
5858
# Provide a unique test method name, starting with 'test_'.
5959

60+
@pytest.mark.skipif(importErr, reason=reasonErr)
61+
def test_no_image_zero_msg():
62+
"""
63+
**Validation Test Description**
64+
65+
This module tests that when no image is present (no filename and no input message),
66+
the camera module properly outputs a zeroed-out message.
67+
68+
**Description of Variables Being Tested**
69+
70+
The camera output message should have all fields set to zero/empty when no image is available.
71+
This ensures that downstream modules receive valid (though empty) data.
72+
73+
- ``imageOutMsg.valid`` should be 0
74+
- ``imageOutMsg.imageBufferLength`` should be 0
75+
- ``imageOutMsg.imagePointer`` should be nullptr
76+
"""
77+
testFailCount = 0
78+
testMessages = []
79+
80+
unitTaskName = "unitTask"
81+
unitProcessName = "TestProcess"
82+
83+
# Create a sim module as an empty container
84+
unitTestSim = SimulationBaseClass.SimBaseClass()
85+
86+
# Create test thread
87+
testProcessRate = macros.sec2nano(0.5)
88+
testProc = unitTestSim.CreateNewProcess(unitProcessName)
89+
testProc.addTask(unitTestSim.CreateNewTask(unitTaskName, testProcessRate))
90+
91+
# Construct algorithm and associated C++ container
92+
module = camera.Camera()
93+
module.ModelTag = "cameras"
94+
95+
# Add test module to runtime call list
96+
unitTestSim.AddModelToTask(unitTaskName, module)
97+
98+
# Don't provide filename or input message - this should trigger zeroed output
99+
module.filename = ""
100+
module.cameraIsOn = 1
101+
102+
# Setup logging on the test module output message
103+
dataLog = module.imageOutMsg.recorder()
104+
unitTestSim.AddModelToTask(unitTaskName, dataLog)
105+
106+
# Need to call the self-init and cross-init methods
107+
unitTestSim.InitializeSimulation()
108+
109+
# Set the simulation time
110+
unitTestSim.ConfigureStopTime(macros.sec2nano(0.5))
111+
112+
# Begin the simulation time run
113+
unitTestSim.ExecuteSimulation()
114+
115+
# Check that the output message fields are zeroed
116+
if len(dataLog.valid) == 0:
117+
testFailCount += 1
118+
testMessages.append("No image output message was written")
119+
else:
120+
# Check the last written message
121+
if dataLog.valid[-1] != 0:
122+
testFailCount += 1
123+
testMessages.append(f"imageOutMsg.valid should be 0, but got {dataLog.valid[-1]}")
124+
125+
if dataLog.imageBufferLength[-1] != 0:
126+
testFailCount += 1
127+
testMessages.append(f"imageOutMsg.imageBufferLength should be 0, but got {dataLog.imageBufferLength[-1]}")
128+
129+
if dataLog.timeTag[-1] != 0:
130+
testFailCount += 1
131+
testMessages.append(f"imageOutMsg.timeTag should be 0, but got {dataLog.timeTag[-1]}")
132+
133+
if dataLog.cameraID[-1] != 0:
134+
testFailCount += 1
135+
testMessages.append(f"imageOutMsg.cameraID should be 0, but got {dataLog.cameraID[-1]}")
136+
137+
if dataLog.imageType[-1] != 0:
138+
testFailCount += 1
139+
testMessages.append(f"imageOutMsg.imageType should be 0, but got {dataLog.imageType[-1]}")
140+
141+
assert testFailCount == 0, testMessages
142+
143+
60144
@pytest.mark.skipif(importErr, reason=reasonErr)
61145
@pytest.mark.parametrize("gauss, darkCurrent, saltPepper, cosmic, blurSize", [
62146
(0, 0, 0, 0, 0)

src/simulation/sensors/camera/camera.cpp

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -357,62 +357,56 @@ void Camera::UpdateState(uint64_t currentSimNanos)
357357
imageBuffer = this->imageInMsg();
358358
this->sensorTimeTag = this->imageInMsg.timeWritten();
359359
}
360-
/* Added for debugging purposes*/
360+
361361
if (!this->filename.empty()){
362362
imageCV = imread(this->filename, cv::IMREAD_COLOR);
363-
this->applyFilters(imageCV, blurred);
364-
if (this->saveImages == 1){
365-
if (!cv::imwrite(localPath, blurred)) {
366-
bskLogger.bskLog(BSK_WARNING, "camera: wasn't able to save the camera module image" );
367-
}
368-
}
369363
}
370364
else if(imageBuffer.valid == 1 && imageBuffer.timeTag >= currentSimNanos){
371365
/*! - Recast image pointer to CV type*/
372366
std::vector<unsigned char> vectorBuffer((char*)imageBuffer.imagePointer,
373367
(char*)imageBuffer.imagePointer + imageBuffer.imageBufferLength);
374368
imageCV = cv::imdecode(vectorBuffer, cv::IMREAD_COLOR);
375-
376-
this->applyFilters(imageCV, blurred);
377-
if (this->saveImages == 1){
378-
if (!cv::imwrite(localPath, blurred)) {
379-
bskLogger.bskLog(BSK_WARNING, "camera: wasn't able to save the camera module image" );
380-
}
381-
}
382-
/*! If the permanent image buffer is not populated, it will be equal to null*/
383-
if (this->pointImageOut != nullptr) {
384-
free(this->pointImageOut);
385-
this->pointImageOut = nullptr;
386-
}
387-
/*! - Encode the cv mat into a png for the future modules to decode it the same way */
388-
std::vector<unsigned char> buf;
389-
std::vector<int> compression = {cv::IMWRITE_PNG_COMPRESSION, 0};
390-
if (!cv::imencode(".png", blurred, buf, compression) || buf.empty()) {
391-
bskLogger.bskLog(BSK_ERROR, "camera: failed to encode image output buffer.");
392-
return;
393-
}
394-
if (buf.size() > (size_t)std::numeric_limits<int32_t>::max()) {
395-
bskLogger.bskLog(BSK_ERROR, "camera: encoded image output buffer is too large.");
396-
return;
397-
}
398-
/*! - Output the saved image */
399-
imageOut.valid = 1;
400-
imageOut.timeTag = imageBuffer.timeTag;
401-
imageOut.cameraID = imageBuffer.cameraID;
402-
imageOut.imageType = imageBuffer.imageType;
403-
imageOut.imageBufferLength = (int32_t)buf.size();
404-
this->pointImageOut = malloc(imageOut.imageBufferLength*sizeof(char));
405-
if (this->pointImageOut == nullptr) {
406-
bskLogger.bskLog(BSK_ERROR, "camera: failed to allocate image output buffer.");
407-
return;
408-
}
409-
memcpy(this->pointImageOut, buf.data(), imageOut.imageBufferLength*sizeof(char));
410-
imageOut.imagePointer = this->pointImageOut;
411-
412-
this->imageOutMsg.write(&imageOut, this->moduleID, currentSimNanos);
413369
}
414370
else{
415371
/*! - If no image is present, write zeros in message */
416372
this->imageOutMsg.write(&imageOut, this->moduleID, currentSimNanos);
373+
return;
374+
}
375+
this->applyFilters(imageCV, blurred);
376+
if (this->saveImages == 1){
377+
if (!cv::imwrite(localPath, blurred)) {
378+
bskLogger.bskLog(BSK_WARNING, "camera: wasn't able to save the camera module image" );
379+
}
380+
}
381+
/*! If the permanent image buffer is not populated, it will be equal to null*/
382+
if (this->pointImageOut != nullptr) {
383+
free(this->pointImageOut);
384+
this->pointImageOut = nullptr;
417385
}
386+
/*! - Encode the cv mat into a png for the future modules to decode it the same way */
387+
std::vector<unsigned char> buf;
388+
std::vector<int> compression = {cv::IMWRITE_PNG_COMPRESSION, 0};
389+
if (!cv::imencode(".png", blurred, buf, compression) || buf.empty()) {
390+
bskLogger.bskLog(BSK_ERROR, "camera: failed to encode image output buffer.");
391+
return;
392+
}
393+
if (buf.size() > (size_t)std::numeric_limits<int32_t>::max()) {
394+
bskLogger.bskLog(BSK_ERROR, "camera: encoded image output buffer is too large.");
395+
return;
396+
}
397+
/*! - Output the saved image */
398+
imageOut.valid = 1;
399+
imageOut.timeTag = imageBuffer.timeTag;
400+
imageOut.cameraID = imageBuffer.cameraID;
401+
imageOut.imageType = imageBuffer.imageType;
402+
imageOut.imageBufferLength = (int32_t)buf.size();
403+
this->pointImageOut = malloc(imageOut.imageBufferLength*sizeof(char));
404+
if (this->pointImageOut == nullptr) {
405+
bskLogger.bskLog(BSK_ERROR, "camera: failed to allocate image output buffer.");
406+
return;
407+
}
408+
memcpy(this->pointImageOut, buf.data(), imageOut.imageBufferLength*sizeof(char));
409+
imageOut.imagePointer = this->pointImageOut;
410+
411+
this->imageOutMsg.write(&imageOut, this->moduleID, currentSimNanos);
418412
}

0 commit comments

Comments
 (0)