25using namespace helios;
30 helios_runtime_error(
"ERROR (RadiationModel::addRadiationCamera): Field of view aspect ratio must be greater than 0.");
31 }
else if (antialiasing_samples == 0) {
32 helios_runtime_error(
"ERROR (RadiationModel::addRadiationCamera): The model requires at least 1 antialiasing sample to run.");
34 helios_runtime_error(
"ERROR (RadiationModel::addRadiationCamera): Camera resolution must be at least 1x1.");
35 }
else if (camera_properties.
HFOV < 0 || camera_properties.
HFOV > 180.f) {
36 helios_runtime_error(
"ERROR (RadiationModel::addRadiationCamera): Camera horizontal field of view must be between 0 and 180 degrees.");
39 RadiationCamera camera(camera_label, band_label, position, lookat, camera_properties, antialiasing_samples);
40 if (cameras.find(camera_label) == cameras.end()) {
41 cameras.emplace(camera_label, camera);
44 std::cout <<
"Camera with label " << camera_label <<
"already exists. Existing properties will be replaced by new inputs." << std::endl;
46 cameras.erase(camera_label);
47 cameras.emplace(camera_label, camera);
50 if (iscameravisualizationenabled) {
51 buildCameraModelGeometry(camera_label);
54 radiativepropertiesneedupdate =
true;
58 uint antialiasing_samples) {
61 addRadiationCamera(camera_label, band_label, position, lookat, camera_properties, antialiasing_samples);
65 if (cameras.find(camera_label) == cameras.end()) {
66 helios_runtime_error(
"ERROR (setCameraSpectralResponse): Camera '" + camera_label +
"' does not exist.");
68 helios_runtime_error(
"ERROR (setCameraSpectralResponse): Band '" + band_label +
"' does not exist.");
71 cameras.at(camera_label).band_spectral_response[band_label] = global_data;
73 radiativepropertiesneedupdate =
true;
78 if (cameras.find(camera_label) == cameras.end()) {
79 helios_runtime_error(
"ERROR (setCameraSpectralResponseFromLibrary): Camera '" + camera_label +
"' does not exist.");
82 const auto &band_labels = cameras.at(camera_label).band_labels;
85 context->
loadXML(
"plugins/radiation/spectral_data/camera_spectral_library.xml");
88 for (
const auto &band: band_labels) {
89 std::string response_spectrum = camera_library_name +
"_" + band;
91 helios_runtime_error(
"ERROR (setCameraSpectralResponseFromLibrary): Band '" + band +
"' referenced in spectral library camera " + camera_library_name +
" does not exist for camera '" + camera_label +
"'.");
94 cameras.at(camera_label).band_spectral_response[band] = response_spectrum;
97 radiativepropertiesneedupdate =
true;
101 if (cameras.find(camera_label) == cameras.end()) {
102 helios_runtime_error(
"ERROR (RadiationModel::setCameraPosition): Camera '" + camera_label +
"' does not exist.");
103 }
else if (position == cameras.at(camera_label).lookat) {
104 helios_runtime_error(
"ERROR (RadiationModel::setCameraPosition): Camera position cannot be equal to the 'lookat' position.");
107 cameras.at(camera_label).position = position;
109 if (iscameravisualizationenabled) {
110 updateCameraModelPosition(camera_label);
116 if (cameras.find(camera_label) == cameras.end()) {
117 helios_runtime_error(
"ERROR (RadiationModel::getCameraPosition): Camera '" + camera_label +
"' does not exist.");
120 return cameras.at(camera_label).position;
124 if (cameras.find(camera_label) == cameras.end()) {
125 helios_runtime_error(
"ERROR (RadiationModel::setCameraLookat): Camera '" + camera_label +
"' does not exist.");
128 cameras.at(camera_label).lookat = lookat;
130 if (iscameravisualizationenabled) {
131 updateCameraModelPosition(camera_label);
137 if (cameras.find(camera_label) == cameras.end()) {
138 helios_runtime_error(
"ERROR (RadiationModel::getCameraLookat): Camera '" + camera_label +
"' does not exist.");
141 return cameras.at(camera_label).lookat;
145 if (cameras.find(camera_label) == cameras.end()) {
146 helios_runtime_error(
"ERROR (RadiationModel::setCameraOrientation): Camera '" + camera_label +
"' does not exist.");
149 cameras.at(camera_label).lookat = cameras.at(camera_label).position + direction;
151 if (iscameravisualizationenabled) {
152 updateCameraModelPosition(camera_label);
158 if (cameras.find(camera_label) == cameras.end()) {
159 helios_runtime_error(
"ERROR (RadiationModel::getCameraOrientation): Camera '" + camera_label +
"' does not exist.");
162 return cart2sphere(cameras.at(camera_label).lookat - cameras.at(camera_label).position);
166 if (cameras.find(camera_label) == cameras.end()) {
167 helios_runtime_error(
"ERROR (RadiationModel::setCameraOrientation): Camera '" + camera_label +
"' does not exist.");
170 cameras.at(camera_label).lookat = cameras.at(camera_label).position +
sphere2cart(direction);
172 if (iscameravisualizationenabled) {
173 updateCameraModelPosition(camera_label);
178 std::vector<std::string> labels(cameras.size());
180 for (
const auto &camera: cameras) {
181 labels.at(cam) = camera.second.label;
187std::string
RadiationModel::writeCameraImage(
const std::string &camera,
const std::vector<std::string> &bands,
const std::string &imagefile_base,
const std::string &image_path,
int frame,
float flux_to_pixel_conversion) {
190 if (cameras.find(camera) == cameras.end()) {
191 std::cout <<
"ERROR (RadiationModel::writeCameraImage): camera with label " << camera <<
" does not exist. Skipping image write for this camera." << std::endl;
195 if (bands.size() != 1 && bands.size() != 3) {
196 helios_runtime_error(
"ERROR (RadiationModel::writeCameraImage): input vector of band labels should either have length of 1 (grayscale image) or length of 3 (RGB image). Skipping image write for this camera.");
199 std::vector<std::vector<float>> camera_data(bands.size());
202 for (
const auto &band: bands) {
205 if (std::find(cameras.at(camera).band_labels.begin(), cameras.at(camera).band_labels.end(), band) == cameras.at(camera).band_labels.end()) {
206 std::cout <<
"ERROR (RadiationModel::writeCameraImage): camera " << camera <<
" band with label " << band <<
" does not exist. Skipping image write for this camera." << std::endl;
219 camera_data.at(b) = cameras.at(camera).pixel_data.at(band);
224 std::string frame_str;
226 frame_str = std::to_string(frame);
229 std::string output_path = image_path;
231 helios_runtime_error(
"ERROR (RadiationModel::writeCameraImage): Invalid image output directory '" + image_path +
"'. Check that the path exists and that you have write permission.");
233 helios_runtime_error(
"ERROR(RadiationModel::writeCameraImage): Image output directory contains a filename. This argument should be the path to a directory not a file.");
236 std::ostringstream outfile;
237 outfile << output_path;
240 outfile << camera <<
"_" << imagefile_base <<
"_" << std::setw(5) << std::setfill(
'0') << frame_str <<
".jpeg";
242 outfile << camera <<
"_" << imagefile_base <<
".jpeg";
244 std::ofstream testfile(outfile.str());
246 if (!testfile.is_open()) {
247 std::cout <<
"ERROR (RadiationModel::writeCameraImage): image file " << outfile.str() <<
" could not be opened. Check that the path exists and that you have write permission. Skipping image write for this camera." << std::endl;
252 int2 camera_resolution = cameras.at(camera).resolution;
254 std::vector<RGBcolor> pixel_data_RGB(camera_resolution.
x * camera_resolution.
y);
257 for (
uint j = 0; j < camera_resolution.
y; j++) {
258 for (
uint i = 0; i < camera_resolution.
x; i++) {
259 if (camera_data.size() == 1) {
260 float c = camera_data.front().at(j * camera_resolution.
x + i);
263 pixel_color =
make_RGBcolor(camera_data.at(0).at(j * camera_resolution.
x + i), camera_data.at(1).at(j * camera_resolution.
x + i), camera_data.at(2).at(j * camera_resolution.
x + i));
265 pixel_color.
scale(flux_to_pixel_conversion);
266 uint ii = camera_resolution.
x - i - 1;
267 uint jj = camera_resolution.
y - j - 1;
268 pixel_data_RGB.at(jj * camera_resolution.
x + ii) = pixel_color;
272 writeJPEG(outfile.str(), camera_resolution.
x, camera_resolution.
y, pixel_data_RGB);
274 return outfile.str();
277std::string
RadiationModel::writeNormCameraImage(
const std::string &camera,
const std::vector<std::string> &bands,
const std::string &imagefile_base,
const std::string &image_path,
int frame) {
280 for (
const std::string &band: bands) {
281 std::string global_data_label =
"camera_" + camera +
"_" + band;
282 if (std::find(cameras.at(camera).band_labels.begin(), cameras.at(camera).band_labels.end(), band) == cameras.at(camera).band_labels.end()) {
283 std::cout <<
"ERROR (RadiationModel::writeNormCameraImage): camera " << camera <<
" band with label " << band <<
" does not exist. Skipping image write for this camera." << std::endl;
286 std::cout <<
"ERROR (RadiationModel::writeNormCameraImage): image data for camera " << camera <<
", band " << band <<
" has not been created. Did you run the radiation model? Skipping image write for this camera." << std::endl;
289 std::vector<float> cameradata;
290 context->
getGlobalData(global_data_label.c_str(), cameradata);
291 for (
float val: cameradata) {
298 for (
const std::string &band: bands) {
299 std::string global_data_label =
"camera_" + camera +
"_" + band;
300 std::vector<float> cameradata;
301 context->
getGlobalData(global_data_label.c_str(), cameradata);
302 for (
float &val: cameradata) {
305 context->
setGlobalData(global_data_label.c_str(), cameradata);
314 if (cameras.find(camera) == cameras.end()) {
315 std::cout <<
"ERROR (RadiationModel::writeCameraImageData): camera with label " << camera <<
" does not exist. Skipping image write for this camera." << std::endl;
319 std::vector<float> camera_data;
322 if (std::find(cameras.at(camera).band_labels.begin(), cameras.at(camera).band_labels.end(), band) == cameras.at(camera).band_labels.end()) {
323 std::cout <<
"ERROR (RadiationModel::writeCameraImageData): camera " << camera <<
" band with label " << band <<
" does not exist. Skipping image write for this camera." << std::endl;
327 std::string global_data_label =
"camera_" + camera +
"_" + band;
330 std::cout <<
"ERROR (RadiationModel::writeCameraImageData): image data for camera " << camera <<
", band " << band <<
" has not been created. Did you run the radiation model? Skipping image write for this camera." << std::endl;
334 context->
getGlobalData(global_data_label.c_str(), camera_data);
336 std::string frame_str;
338 frame_str = std::to_string(frame);
341 std::string output_path = image_path;
343 helios_runtime_error(
"ERROR (RadiationModel::writeCameraImage): Invalid image output directory '" + image_path +
"'. Check that the path exists and that you have write permission.");
345 helios_runtime_error(
"ERROR(RadiationModel::writeCameraImage): Image output directory contains a filename. This argument should be the path to a directory not a file.");
348 std::ostringstream outfile;
349 outfile << output_path;
352 outfile << camera <<
"_" << imagefile_base <<
"_" << std::setw(5) << std::setfill(
'0') << frame_str <<
".txt";
354 outfile << camera <<
"_" << imagefile_base <<
".txt";
357 std::ofstream outfilestream(outfile.str());
359 if (!outfilestream.is_open()) {
360 std::cout <<
"ERROR (RadiationModel::writeCameraImageData): image file " << outfile.str() <<
" could not be opened. Check that the path exists and that you have write permission. Skipping image write for this camera." << std::endl;
364 int2 camera_resolution = cameras.at(camera).resolution;
366 for (
int j = 0; j < camera_resolution.
y; j++) {
367 for (
int i = camera_resolution.
x - 1; i >= 0; i--) {
368 outfilestream << camera_data.at(j * camera_resolution.
x + i) <<
" ";
370 outfilestream <<
"\n";
373 outfilestream.close();
378 calibration_flag =
true;
382 const std::vector<std::vector<float>> &truevalues,
const std::string &calibratedmark) {
384 std::vector<std::string> objectlabels;
385 vec2 wavelengthrange_c = wavelengthrange;
386 cameracalibration->
preprocessSpectra(sourcelabels_raw, cameraresponselabels, objectlabels, wavelengthrange_c);
390 cameraproperties.
HFOV = calibratecamera.HFOV_degrees;
393 cameraproperties.
lens_diameter = calibratecamera.lens_diameter;
397 std::string cameralabel =
"calibration";
398 std::map<uint, std::vector<vec2>> simulatedcolorboardspectra;
399 for (
uint UUID: UUIDs_target) {
400 simulatedcolorboardspectra.emplace(UUID, NULL);
403 for (
uint ID = 0; ID < radiation_sources.size(); ID++) {
407 std::vector<float> wavelengths;
409 int numberwavelengths = wavelengths.size();
411 for (
int iw = 0; iw < numberwavelengths; iw++) {
412 std::string wavelengthlabel = std::to_string(wavelengths.at(iw));
414 std::vector<std::string> sourcelabels;
415 for (std::string sourcelabel_raw: sourcelabels_raw) {
416 std::vector<vec2> icalsource;
417 icalsource.push_back(cameracalibration->processedspectra.at(
"source").at(sourcelabel_raw).at(iw));
418 icalsource.push_back(cameracalibration->processedspectra.at(
"source").at(sourcelabel_raw).at(iw));
419 icalsource.at(1).x += 1;
420 std::string sourcelable =
"Cal_source_" + sourcelabel_raw;
421 sourcelabels.push_back(sourcelable);
425 std::vector<vec2> icalcamera(2);
426 icalcamera.at(0).y = 1;
427 icalcamera.at(1).y = 1;
428 icalcamera.at(0).x = wavelengths.at(iw);
429 icalcamera.at(1).x = wavelengths.at(iw) + 1;
430 std::string camlable =
"Cal_cameraresponse";
433 for (
auto objectpair: cameracalibration->processedspectra.at(
"object")) {
434 std::vector<vec2> spectrum_obj;
435 spectrum_obj.push_back(objectpair.second.at(iw));
436 spectrum_obj.push_back(objectpair.second.at(iw));
437 spectrum_obj.at(1).x += 1;
438 context->
setGlobalData(objectpair.first.c_str(), spectrum_obj);
445 for (std::string sourcelabel_raw: sourcelabels_raw) {
459 std::vector<float> camera_data;
460 std::string global_data_label =
"camera_" + cameralabel +
"_" + wavelengthlabel;
461 context->
getGlobalData(global_data_label.c_str(), camera_data);
463 std::vector<uint> pixel_labels;
464 std::string global_data_label_UUID =
"camera_" + cameralabel +
"_pixel_UUID";
465 context->
getGlobalData(global_data_label_UUID.c_str(), pixel_labels);
467 for (
uint j = 0; j < calibratecamera.resolution.
y; j++) {
468 for (
uint i = 0; i < calibratecamera.resolution.
x; i++) {
469 float icdata = camera_data.at(j * calibratecamera.resolution.
x + i);
471 uint UUID = pixel_labels.at(j * calibratecamera.resolution.
x + i) - 1;
472 if (find(UUIDs_target.begin(), UUIDs_target.end(), UUID) != UUIDs_target.end()) {
473 if (simulatedcolorboardspectra.at(UUID).empty()) {
474 simulatedcolorboardspectra.at(UUID).push_back(
make_vec2(wavelengths.at(iw), icdata /
float(numberwavelengths)));
475 }
else if (simulatedcolorboardspectra.at(UUID).back().x == wavelengths.at(iw)) {
476 simulatedcolorboardspectra.at(UUID).back().y += icdata / float(numberwavelengths);
477 }
else if (simulatedcolorboardspectra.at(UUID).back().x != wavelengths.at(iw)) {
478 simulatedcolorboardspectra.at(UUID).push_back(
make_vec2(wavelengths.at(iw), icdata /
float(numberwavelengths)));
488 for (
uint UUID: UUIDs_colorbd) {
489 std::string colorboardspectra;
490 context->
getPrimitiveData(UUID,
"reflectivity_spectrum", colorboardspectra);
491 context->
setPrimitiveData(UUID,
"reflectivity_spectrum", colorboardspectra +
"_raw");
496 float fluxscale,
float diffusefactor,
uint scatteringdepth) {
498 float sources_fluxsum = 0;
499 std::vector<float> sources_fluxes;
500 for (
uint ID = 0; ID < sourcelabels.size(); ID++) {
501 std::vector<vec2> Source_spectrum = loadSpectralData(sourcelabels.at(ID).c_str());
505 sources_fluxsum += sources_fluxes.at(ID);
510 for (
uint ID = 0; ID < radiation_sources.size(); ID++) {
517 if (bandlabels.size() > 1) {
518 for (
int iband = 1; iband < bandlabels.size(); iband++) {
520 for (
uint ID = 0; ID < radiation_sources.size(); ID++) {
527 for (
int iband = 0; iband < bandlabels.size(); iband++) {
535void RadiationModel::runRadiationImaging(
const std::vector<std::string> &cameralabels,
const std::vector<std::string> &sourcelabels,
const std::vector<std::string> &bandlabels,
const std::vector<std::string> &cameraresponselabels,
536 helios::vec2 wavelengthrange,
float fluxscale,
float diffusefactor,
uint scatteringdepth) {
538 float sources_fluxsum = 0;
539 std::vector<float> sources_fluxes;
540 for (
uint ID = 0; ID < sourcelabels.size(); ID++) {
541 std::vector<vec2> Source_spectrum = loadSpectralData(sourcelabels.at(ID).c_str());
545 sources_fluxsum += sources_fluxes.at(ID);
550 for (
uint ID = 0; ID < radiation_sources.size(); ID++) {
557 if (bandlabels.size() > 1) {
558 for (
int iband = 1; iband < bandlabels.size(); iband++) {
560 for (
uint ID = 0; ID < radiation_sources.size(); ID++) {
567 for (
int ic = 0; ic < cameralabels.size(); ic++) {
568 for (
int iband = 0; iband < bandlabels.size(); iband++) {
578float RadiationModel::getCameraResponseScale(
const std::string &orginalcameralabel,
const std::vector<std::string> &cameraresponselabels,
const std::vector<std::string> &bandlabels,
const std::vector<std::string> &sourcelabels,
vec2 &wavelengthrange,
579 const std::vector<std::vector<float>> &truevalues) {
584 cameraproperties.
HFOV = calibratecamera.HFOV_degrees;
587 cameraproperties.
lens_diameter = calibratecamera.lens_diameter;
590 std::string cameralabel = orginalcameralabel +
"Scale";
602 if (cameras.find(cameralabel) == cameras.end()) {
603 helios_runtime_error(
"ERROR (RadiationModel::writePrimitiveDataLabelMap): Camera '" + cameralabel +
"' does not exist.");
607 std::vector<uint> camera_UUIDs;
608 std::string global_data_label =
"camera_" + cameralabel +
"_pixel_UUID";
610 helios_runtime_error(
"ERROR (RadiationModel::writePrimitiveDataLabelMap): Pixel labels for camera '" + cameralabel +
"' do not exist. Was the radiation model run to generate labels?");
612 context->
getGlobalData(global_data_label.c_str(), camera_UUIDs);
613 std::vector<uint> pixel_UUIDs = camera_UUIDs;
614 int2 camera_resolution = cameras.at(cameralabel).resolution;
616 std::string frame_str;
618 frame_str = std::to_string(frame);
621 std::string output_path = image_path;
623 helios_runtime_error(
"ERROR (RadiationModel::writePrimitiveDataLabelMap): Invalid image output directory '" + image_path +
"'. Check that the path exists and that you have write permission.");
625 helios_runtime_error(
"ERROR(RadiationModel::writePrimitiveDataLabelMap): Image output directory contains a filename. This argument should be the path to a directory not a file.");
628 std::ostringstream outfile;
629 outfile << output_path;
632 outfile << cameralabel <<
"_" << imagefile_base <<
"_" << std::setw(5) << std::setfill(
'0') << frame_str <<
".txt";
634 outfile << cameralabel <<
"_" << imagefile_base <<
".txt";
638 std::ofstream pixel_data(outfile.str());
640 if (!pixel_data.is_open()) {
641 helios_runtime_error(
"ERROR (RadiationModel::writePrimitiveDataLabelMap): Could not open file '" + outfile.str() +
"' for writing.");
644 bool empty_flag =
true;
645 for (
uint j = 0; j < camera_resolution.
y; j++) {
646 for (
uint i = 0; i < camera_resolution.
x; i++) {
647 uint ii = camera_resolution.
x - i - 1;
648 uint UUID = pixel_UUIDs.at(j * camera_resolution.
x + ii) - 1;
654 pixel_data << labeldata <<
" ";
659 pixel_data << labeldata <<
" ";
664 pixel_data << labeldata <<
" ";
669 pixel_data << labeldata <<
" ";
672 pixel_data << padvalue <<
" ";
675 pixel_data << padvalue <<
" ";
683 std::cerr <<
"WARNING (RadiationModel::writePrimitiveDataLabelMap): No primitive data of " << primitive_data_label <<
" found in camera image. Primitive data map contains only padded values." << std::endl;
687void RadiationModel::writeObjectDataLabelMap(
const std::string &cameralabel,
const std::string &object_data_label,
const std::string &imagefile_base,
const std::string &image_path,
int frame,
float padvalue) {
689 if (cameras.find(cameralabel) == cameras.end()) {
690 helios_runtime_error(
"ERROR (RadiationModel::writeObjectDataLabelMap): Camera '" + cameralabel +
"' does not exist.");
694 std::vector<uint> camera_UUIDs;
695 std::string global_data_label =
"camera_" + cameralabel +
"_pixel_UUID";
697 helios_runtime_error(
"ERROR (RadiationModel::writeObjectDataLabelMap): Pixel labels for camera '" + cameralabel +
"' do not exist. Was the radiation model run to generate labels?");
699 context->
getGlobalData(global_data_label.c_str(), camera_UUIDs);
700 std::vector<uint> pixel_UUIDs = camera_UUIDs;
701 int2 camera_resolution = cameras.at(cameralabel).resolution;
703 std::string frame_str;
705 frame_str = std::to_string(frame);
708 std::string output_path = image_path;
710 helios_runtime_error(
"ERROR (RadiationModel::writeObjectDataLabelMap): Invalid image output directory '" + image_path +
"'. Check that the path exists and that you have write permission.");
712 helios_runtime_error(
"ERROR(RadiationModel::writeObjectDataLabelMap): Image output directory contains a filename. This argument should be the path to a directory not a file.");
715 std::ostringstream outfile;
716 outfile << output_path;
719 outfile << cameralabel <<
"_" << imagefile_base <<
"_" << std::setw(5) << std::setfill(
'0') << frame_str <<
".txt";
721 outfile << cameralabel <<
"_" << imagefile_base <<
".txt";
725 std::ofstream pixel_data(outfile.str());
727 if (!pixel_data.is_open()) {
728 helios_runtime_error(
"ERROR (RadiationModel::writeObjectDataLabelMap): Could not open file '" + outfile.str() +
"' for writing.");
731 bool empty_flag =
true;
732 for (
uint j = 0; j < camera_resolution.
y; j++) {
733 for (
uint i = 0; i < camera_resolution.
x; i++) {
734 uint ii = camera_resolution.
x - i - 1;
735 uint UUID = pixel_UUIDs.at(j * camera_resolution.
x + ii) - 1;
737 pixel_data << padvalue <<
" ";
745 context->
getObjectData(objID, object_data_label.c_str(), labeldata);
746 pixel_data << labeldata <<
" ";
750 context->
getObjectData(objID, object_data_label.c_str(), labeldata);
751 pixel_data << labeldata <<
" ";
755 context->
getObjectData(objID, object_data_label.c_str(), labeldata);
756 pixel_data << labeldata <<
" ";
760 context->
getObjectData(objID, object_data_label.c_str(), labeldata);
761 pixel_data << labeldata <<
" ";
764 pixel_data << padvalue <<
" ";
767 pixel_data << padvalue <<
" ";
775 std::cerr <<
"WARNING (RadiationModel::writeObjectDataLabelMap): No object data of " << object_data_label <<
" found in camera image. Object data map contains only padded values." << std::endl;
781 if (cameras.find(cameralabel) == cameras.end()) {
782 helios_runtime_error(
"ERROR (RadiationModel::writeDepthImageData): Camera '" + cameralabel +
"' does not exist.");
785 std::string global_data_label =
"camera_" + cameralabel +
"_pixel_depth";
787 helios_runtime_error(
"ERROR (RadiationModel::writeDepthImageData): Depth data for camera '" + cameralabel +
"' does not exist. Was the radiation model run for the camera?");
789 std::vector<float> camera_depth;
790 context->
getGlobalData(global_data_label.c_str(), camera_depth);
791 helios::vec3 camera_position = cameras.at(cameralabel).position;
792 helios::vec3 camera_lookat = cameras.at(cameralabel).lookat;
794 int2 camera_resolution = cameras.at(cameralabel).resolution;
796 std::string frame_str;
798 frame_str = std::to_string(frame);
801 std::string output_path = image_path;
803 helios_runtime_error(
"ERROR (RadiationModel::writeDepthImageData): Invalid image output directory '" + image_path +
"'. Check that the path exists and that you have write permission.");
805 helios_runtime_error(
"ERROR(RadiationModel::writeDepthImageData): Image output directory contains a filename. This argument should be the path to a directory not a file.");
808 std::ostringstream outfile;
809 outfile << output_path;
812 outfile << cameralabel <<
"_" << imagefile_base <<
"_" << std::setw(5) << std::setfill(
'0') << frame_str <<
".txt";
814 outfile << cameralabel <<
"_" << imagefile_base <<
".txt";
818 std::ofstream pixel_data(outfile.str());
820 if (!pixel_data.is_open()) {
821 helios_runtime_error(
"ERROR (RadiationModel::writeDepthImageData): Could not open file '" + outfile.str() +
"' for writing.");
824 for (
int j = 0; j < camera_resolution.
y; j++) {
825 for (
int i = camera_resolution.
x - 1; i >= 0; i--) {
826 pixel_data << camera_depth.at(j * camera_resolution.
x + i) <<
" ";
836 if (cameras.find(cameralabel) == cameras.end()) {
837 helios_runtime_error(
"ERROR (RadiationModel::writeNormDepthImage): Camera '" + cameralabel +
"' does not exist.");
840 std::string global_data_label =
"camera_" + cameralabel +
"_pixel_depth";
842 helios_runtime_error(
"ERROR (RadiationModel::writeNormDepthImage): Depth data for camera '" + cameralabel +
"' does not exist. Was the radiation model run for the camera?");
844 std::vector<float> camera_depth;
845 context->
getGlobalData(global_data_label.c_str(), camera_depth);
846 helios::vec3 camera_position = cameras.at(cameralabel).position;
847 helios::vec3 camera_lookat = cameras.at(cameralabel).lookat;
849 int2 camera_resolution = cameras.at(cameralabel).resolution;
851 std::string frame_str;
853 frame_str = std::to_string(frame);
856 std::string output_path = image_path;
858 helios_runtime_error(
"ERROR (RadiationModel::writeNormDepthImage): Invalid image output directory '" + image_path +
"'. Check that the path exists and that you have write permission.");
860 helios_runtime_error(
"ERROR(RadiationModel::writeNormDepthImage): Image output directory contains a filename. This argument should be the path to a directory not a file.");
863 std::ostringstream outfile;
864 outfile << output_path;
867 outfile << cameralabel <<
"_" << imagefile_base <<
"_" << std::setw(5) << std::setfill(
'0') << frame_str <<
".jpeg";
869 outfile << cameralabel <<
"_" << imagefile_base <<
".jpeg";
872 float min_depth = 99999;
873 for (
int i = 0; i < camera_depth.size(); i++) {
874 if (camera_depth.at(i) < 0 || camera_depth.at(i) > max_depth) {
875 camera_depth.at(i) = max_depth;
877 if (camera_depth.at(i) < min_depth) {
878 min_depth = camera_depth.at(i);
881 for (
int i = 0; i < camera_depth.size(); i++) {
882 camera_depth.at(i) = 1.f - (camera_depth.at(i) - min_depth) / (max_depth - min_depth);
885 std::vector<RGBcolor> pixel_data(camera_resolution.
x * camera_resolution.
y);
888 for (
uint j = 0; j < camera_resolution.
y; j++) {
889 for (
uint i = 0; i < camera_resolution.
x; i++) {
891 float c = camera_depth.at(j * camera_resolution.
x + i);
894 uint ii = camera_resolution.
x - i - 1;
895 uint jj = camera_resolution.
y - j - 1;
896 pixel_data.at(jj * camera_resolution.
x + ii) = pixel_color;
900 writeJPEG(outfile.str(), camera_resolution.
x, camera_resolution.
y, pixel_data);
904void RadiationModel::writeImageBoundingBoxes(
const std::string &cameralabel,
const std::string &primitive_data_label,
uint object_class_ID,
const std::string &imagefile_base,
const std::string &image_path,
bool append_label_file,
int frame) {
906 if (cameras.find(cameralabel) == cameras.end()) {
907 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes): Camera '" + cameralabel +
"' does not exist.");
911 std::vector<uint> camera_UUIDs;
912 std::string global_data_label =
"camera_" + cameralabel +
"_pixel_UUID";
914 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes): Pixel labels for camera '" + cameralabel +
"' do not exist. Was the radiation model run to generate labels?");
916 context->
getGlobalData(global_data_label.c_str(), camera_UUIDs);
917 std::vector<uint> pixel_UUIDs = camera_UUIDs;
918 int2 camera_resolution = cameras.at(cameralabel).resolution;
920 std::string frame_str;
922 frame_str = std::to_string(frame);
925 std::string output_path = image_path;
927 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes): Invalid image output directory '" + image_path +
"'. Check that the path exists and that you have write permission.");
929 helios_runtime_error(
"ERROR(RadiationModel::writeImageBoundingBoxes): Image output directory contains a filename. This argument should be the path to a directory not a file.");
932 std::ostringstream outfile;
933 outfile << output_path;
936 outfile << cameralabel <<
"_" << imagefile_base <<
"_" << std::setw(5) << std::setfill(
'0') << frame_str <<
".txt";
938 outfile << cameralabel <<
"_" << imagefile_base <<
".txt";
942 std::ofstream label_file;
943 if (append_label_file) {
944 label_file.open(outfile.str(), std::ios::out | std::ios::app);
946 label_file.open(outfile.str());
949 if (!label_file.is_open()) {
950 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes): Could not open file '" + outfile.str() +
"'.");
953 std::map<int, vec4> pdata_bounds;
955 for (
int j = 0; j < camera_resolution.
y; j++) {
956 for (
int i = 0; i < camera_resolution.
x; i++) {
957 uint ii = camera_resolution.
x - i - 1;
958 uint UUID = pixel_UUIDs.at(j * camera_resolution.
x + ii) - 1;
964 if (datatype == HELIOS_TYPE_UINT) {
966 context->
getPrimitiveData(UUID, primitive_data_label.c_str(), labeldata_ui);
967 labeldata = labeldata_ui;
968 }
else if (datatype == HELIOS_TYPE_INT) {
971 labeldata = (
uint) labeldata_i;
976 if (pdata_bounds.find(labeldata) == pdata_bounds.end()) {
977 pdata_bounds[labeldata] =
make_vec4(1e6, -1, 1e6, -1);
980 if (i < pdata_bounds[labeldata].x) {
981 pdata_bounds[labeldata].x = i;
983 if (i > pdata_bounds[labeldata].y) {
984 pdata_bounds[labeldata].y = i;
986 if (j < pdata_bounds[labeldata].z) {
987 pdata_bounds[labeldata].z = j;
989 if (j > pdata_bounds[labeldata].w) {
990 pdata_bounds[labeldata].w = j;
996 for (
auto box: pdata_bounds) {
997 vec4 bbox = box.second;
998 if (bbox.
x == bbox.
y || bbox.
z == bbox.
w) {
1001 label_file << object_class_ID <<
" " << (bbox.
x + 0.5 * (bbox.
y - bbox.
x)) / float(camera_resolution.
x) <<
" " << (bbox.
z + 0.5 * (bbox.
w - bbox.
z)) / float(camera_resolution.
y) <<
" " << std::setprecision(6) << std::fixed
1002 << (bbox.
y - bbox.
x) /
float(camera_resolution.
x) <<
" " << (bbox.
w - bbox.
z) /
float(camera_resolution.
y) << std::endl;
1011 if (cameras.find(cameralabel) == cameras.end()) {
1012 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes_ObjectData): Camera '" + cameralabel +
"' does not exist.");
1016 std::vector<uint> camera_UUIDs;
1017 std::string global_data_label =
"camera_" + cameralabel +
"_pixel_UUID";
1019 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes_ObjectData): Pixel labels for camera '" + cameralabel +
"' do not exist. Was the radiation model run to generate labels?");
1021 context->
getGlobalData(global_data_label.c_str(), camera_UUIDs);
1022 std::vector<uint> pixel_UUIDs = camera_UUIDs;
1023 int2 camera_resolution = cameras.at(cameralabel).resolution;
1025 std::string frame_str;
1027 frame_str = std::to_string(frame);
1030 std::string output_path = image_path;
1032 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes_ObjectData): Invalid image output directory '" + image_path +
"'. Check that the path exists and that you have write permission.");
1034 helios_runtime_error(
"ERROR(RadiationModel::writeImageBoundingBoxes_ObjectData): Image output directory contains a filename. This argument should be the path to a directory not a file.");
1037 std::ostringstream outfile;
1038 outfile << output_path;
1041 outfile << cameralabel <<
"_" << imagefile_base <<
"_" << std::setw(5) << std::setfill(
'0') << frame_str <<
".txt";
1043 outfile << cameralabel <<
"_" << imagefile_base <<
".txt";
1047 std::ofstream label_file;
1048 if (append_label_file) {
1049 label_file.open(outfile.str(), std::ios::out | std::ios::app);
1051 label_file.open(outfile.str());
1054 if (!label_file.is_open()) {
1055 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes_ObjectData): Could not open file '" + outfile.str() +
"'.");
1058 std::map<int, vec4> pdata_bounds;
1060 for (
int j = 0; j < camera_resolution.
y; j++) {
1061 for (
int i = 0; i < camera_resolution.
x; i++) {
1062 uint ii = camera_resolution.
x - i - 1;
1063 uint UUID = pixel_UUIDs.at(j * camera_resolution.
x + ii) - 1;
1078 if (datatype == HELIOS_TYPE_UINT) {
1080 context->
getObjectData(objID, object_data_label.c_str(), labeldata_ui);
1081 labeldata = labeldata_ui;
1082 }
else if (datatype == HELIOS_TYPE_INT) {
1084 context->
getObjectData(objID, object_data_label.c_str(), labeldata_i);
1085 labeldata = (
uint) labeldata_i;
1090 if (pdata_bounds.find(labeldata) == pdata_bounds.end()) {
1091 pdata_bounds[labeldata] =
make_vec4(1e6, -1, 1e6, -1);
1094 if (i < pdata_bounds[labeldata].x) {
1095 pdata_bounds[labeldata].x = i;
1097 if (i > pdata_bounds[labeldata].y) {
1098 pdata_bounds[labeldata].y = i;
1100 if (j < pdata_bounds[labeldata].z) {
1101 pdata_bounds[labeldata].z = j;
1103 if (j > pdata_bounds[labeldata].w) {
1104 pdata_bounds[labeldata].w = j;
1109 for (
auto box: pdata_bounds) {
1110 vec4 bbox = box.second;
1111 if (bbox.
x == bbox.
y || bbox.
z == bbox.
w) {
1114 label_file << object_class_ID <<
" " << (bbox.
x + 0.5 * (bbox.
y - bbox.
x)) / float(camera_resolution.
x) <<
" " << (bbox.
z + 0.5 * (bbox.
w - bbox.
z)) / float(camera_resolution.
y) <<
" " << std::setprecision(6) << std::fixed
1115 << (bbox.
y - bbox.
x) /
float(camera_resolution.
x) <<
" " << (bbox.
w - bbox.
z) /
float(camera_resolution.
y) << std::endl;
1121void RadiationModel::writeImageBoundingBoxes(
const std::string &cameralabel,
const std::string &primitive_data_label,
const uint &object_class_ID,
const std::string &image_file,
const std::string &classes_txt_file,
const std::string &image_path) {
1122 writeImageBoundingBoxes(cameralabel, std::vector<std::string>{primitive_data_label}, std::vector<uint>{object_class_ID}, image_file, classes_txt_file, image_path);
1125void RadiationModel::writeImageBoundingBoxes(
const std::string &cameralabel,
const std::vector<std::string> &primitive_data_label,
const std::vector<uint> &object_class_ID,
const std::string &image_file,
const std::string &classes_txt_file,
1126 const std::string &image_path) {
1128 if (cameras.find(cameralabel) == cameras.end()) {
1129 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes): Camera '" + cameralabel +
"' does not exist.");
1132 if (primitive_data_label.size() != object_class_ID.size()) {
1133 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes): The lengths of primitive_data_label and object_class_ID vectors must be the same.");
1137 std::vector<uint> camera_UUIDs;
1138 std::string global_data_label =
"camera_" + cameralabel +
"_pixel_UUID";
1140 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes): Pixel labels for camera '" + cameralabel +
"' do not exist. Was the radiation model run to generate labels?");
1142 context->
getGlobalData(global_data_label.c_str(), camera_UUIDs);
1143 std::vector<uint> pixel_UUIDs = camera_UUIDs;
1144 int2 camera_resolution = cameras.at(cameralabel).resolution;
1146 std::string output_path = image_path;
1148 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes): Invalid image output directory '" + image_path +
"'. Check that the path exists and that you have write permission.");
1150 helios_runtime_error(
"ERROR(RadiationModel::writeImageBoundingBoxes): Image output directory contains a filename. This argument should be the path to a directory not a file.");
1153 std::string outfile_txt = output_path + std::filesystem::path(image_file).stem().string() +
".txt";
1155 std::ofstream label_file(outfile_txt);
1157 if (!label_file.is_open()) {
1158 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes): Could not open output bounding box file '" + outfile_txt +
"'.");
1162 std::map<std::pair<uint, uint>,
vec4> pdata_bounds;
1165 for (
int j = 0; j < camera_resolution.
y; j++) {
1166 for (
int i = 0; i < camera_resolution.
x; i++) {
1167 uint ii = camera_resolution.
x - i - 1;
1168 uint UUID = pixel_UUIDs.at(j * camera_resolution.
x + ii) - 1;
1172 for (
size_t label_idx = 0; label_idx < primitive_data_label.size(); label_idx++) {
1173 const std::string &data_label = primitive_data_label[label_idx];
1174 uint class_id = object_class_ID[label_idx];
1178 bool has_data =
false;
1184 labeldata = labeldata_ui;
1189 labeldata = (
uint) labeldata_i;
1194 std::pair<uint, uint> key = std::make_pair(class_id, labeldata);
1196 if (pdata_bounds.find(key) == pdata_bounds.end()) {
1197 pdata_bounds[key] =
make_vec4(1e6, -1, 1e6, -1);
1200 if (i < pdata_bounds[key].x) {
1201 pdata_bounds[key].x = i;
1203 if (i > pdata_bounds[key].y) {
1204 pdata_bounds[key].y = i;
1206 if (j < pdata_bounds[key].z) {
1207 pdata_bounds[key].z = j;
1209 if (j > pdata_bounds[key].w) {
1210 pdata_bounds[key].w = j;
1219 for (
auto box: pdata_bounds) {
1220 uint class_id = box.first.first;
1221 vec4 bbox = box.second;
1222 if (bbox.
x == bbox.
y || bbox.
z == bbox.
w) {
1225 label_file << class_id <<
" " << (bbox.
x + 0.5 * (bbox.
y - bbox.
x)) / float(camera_resolution.
x) <<
" " << (bbox.
z + 0.5 * (bbox.
w - bbox.
z)) / float(camera_resolution.
y) <<
" " << std::setprecision(6) << std::fixed
1226 << (bbox.
y - bbox.
x) /
float(camera_resolution.
x) <<
" " << (bbox.
w - bbox.
z) /
float(camera_resolution.
y) << std::endl;
1231 std::ofstream classes_txt_stream(output_path + classes_txt_file);
1232 if (!classes_txt_stream.is_open()) {
1233 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes): Could not open output classes file '" + output_path + classes_txt_file +
".");
1235 for (
int i = 0; i < object_class_ID.size(); i++) {
1236 classes_txt_stream << object_class_ID.at(i) <<
" " << primitive_data_label.at(i) << std::endl;
1238 classes_txt_stream.close();
1242 const std::string &image_path) {
1243 writeImageBoundingBoxes_ObjectData(cameralabel, std::vector<std::string>{object_data_label}, std::vector<uint>{object_class_ID}, image_file, classes_txt_file, image_path);
1247 const std::string &image_path) {
1249 if (cameras.find(cameralabel) == cameras.end()) {
1250 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes_ObjectData): Camera '" + cameralabel +
"' does not exist.");
1253 if (object_data_label.size() != object_class_ID.size()) {
1254 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes_ObjectData): The lengths of object_data_label and object_class_ID vectors must be the same.");
1258 std::vector<uint> camera_UUIDs;
1259 std::string global_data_label =
"camera_" + cameralabel +
"_pixel_UUID";
1261 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes_ObjectData): Pixel labels for camera '" + cameralabel +
"' do not exist. Was the radiation model run to generate labels?");
1263 context->
getGlobalData(global_data_label.c_str(), camera_UUIDs);
1264 std::vector<uint> pixel_UUIDs = camera_UUIDs;
1265 int2 camera_resolution = cameras.at(cameralabel).resolution;
1267 std::string output_path = image_path;
1269 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes_ObjectData): Invalid image output directory '" + image_path +
"'. Check that the path exists and that you have write permission.");
1271 helios_runtime_error(
"ERROR(RadiationModel::writeImageBoundingBoxes_ObjectData): Image output directory contains a filename. This argument should be the path to a directory not a file.");
1274 std::string outfile_txt = output_path + std::filesystem::path(image_file).stem().string() +
".txt";
1276 std::ofstream label_file(outfile_txt);
1278 if (!label_file.is_open()) {
1279 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes_ObjectData): Could not open output bounding box file '" + outfile_txt +
"'.");
1283 std::map<std::pair<uint, uint>,
vec4> pdata_bounds;
1286 for (
int j = 0; j < camera_resolution.
y; j++) {
1287 for (
int i = 0; i < camera_resolution.
x; i++) {
1288 uint ii = camera_resolution.
x - i - 1;
1289 uint UUID = pixel_UUIDs.at(j * camera_resolution.
x + ii) - 1;
1302 for (
size_t label_idx = 0; label_idx < object_data_label.size(); label_idx++) {
1303 const std::string &data_label = object_data_label[label_idx];
1304 uint class_id = object_class_ID[label_idx];
1308 bool has_data =
false;
1313 context->
getObjectData(objID, data_label.c_str(), labeldata_ui);
1314 labeldata = labeldata_ui;
1318 context->
getObjectData(objID, data_label.c_str(), labeldata_i);
1319 labeldata = (
uint) labeldata_i;
1324 std::pair<uint, uint> key = std::make_pair(class_id, labeldata);
1326 if (pdata_bounds.find(key) == pdata_bounds.end()) {
1327 pdata_bounds[key] =
make_vec4(1e6, -1, 1e6, -1);
1330 if (i < pdata_bounds[key].x) {
1331 pdata_bounds[key].x = i;
1333 if (i > pdata_bounds[key].y) {
1334 pdata_bounds[key].y = i;
1336 if (j < pdata_bounds[key].z) {
1337 pdata_bounds[key].z = j;
1339 if (j > pdata_bounds[key].w) {
1340 pdata_bounds[key].w = j;
1348 for (
auto box: pdata_bounds) {
1349 uint class_id = box.first.first;
1350 vec4 bbox = box.second;
1351 if (bbox.
x == bbox.
y || bbox.
z == bbox.
w) {
1354 label_file << class_id <<
" " << (bbox.
x + 0.5 * (bbox.
y - bbox.
x)) / float(camera_resolution.
x) <<
" " << (bbox.
z + 0.5 * (bbox.
w - bbox.
z)) / float(camera_resolution.
y) <<
" " << std::setprecision(6) << std::fixed
1355 << (bbox.
y - bbox.
x) /
float(camera_resolution.
x) <<
" " << (bbox.
w - bbox.
z) /
float(camera_resolution.
y) << std::endl;
1360 std::ofstream classes_txt_stream(output_path + classes_txt_file);
1361 if (!classes_txt_stream.is_open()) {
1362 helios_runtime_error(
"ERROR (RadiationModel::writeImageBoundingBoxes_ObjectData): Could not open output classes file '" + output_path + classes_txt_file +
".");
1364 for (
int i = 0; i < object_class_ID.size(); i++) {
1365 classes_txt_stream << object_class_ID.at(i) <<
" " << object_data_label.at(i) << std::endl;
1367 classes_txt_stream.close();
1371std::pair<nlohmann::json, int> RadiationModel::initializeCOCOJsonWithImageId(
const std::string &filename,
bool append_file,
const std::string &cameralabel,
const helios::int2 &camera_resolution,
const std::string &image_file) {
1372 nlohmann::json coco_json;
1376 std::ifstream existing_file(filename);
1377 if (existing_file.is_open()) {
1379 existing_file >> coco_json;
1380 }
catch (
const std::exception &e) {
1383 existing_file.close();
1388 if (coco_json.empty()) {
1389 coco_json[
"categories"] = nlohmann::json::array();
1390 coco_json[
"images"] = nlohmann::json::array();
1391 coco_json[
"annotations"] = nlohmann::json::array();
1395 std::filesystem::path image_path_obj(image_file);
1396 std::string filename_only = image_path_obj.filename().string();
1399 bool image_exists =
false;
1400 for (
const auto &img: coco_json[
"images"]) {
1401 if (img[
"file_name"] == filename_only) {
1402 image_id = img[
"id"];
1403 image_exists =
true;
1409 if (!image_exists) {
1411 int max_image_id = -1;
1412 for (
const auto &img: coco_json[
"images"]) {
1413 if (img[
"id"] > max_image_id) {
1414 max_image_id = img[
"id"];
1417 image_id = max_image_id + 1;
1420 nlohmann::json image_entry;
1421 image_entry[
"id"] = image_id;
1422 image_entry[
"file_name"] = filename_only;
1423 image_entry[
"height"] = camera_resolution.
y;
1424 image_entry[
"width"] = camera_resolution.
x;
1425 coco_json[
"images"].push_back(image_entry);
1428 return std::make_pair(coco_json, image_id);
1432nlohmann::json RadiationModel::initializeCOCOJson(
const std::string &filename,
bool append_file,
const std::string &cameralabel,
const helios::int2 &camera_resolution,
const std::string &image_file) {
1433 return initializeCOCOJsonWithImageId(filename, append_file, cameralabel, camera_resolution, image_file).first;
1437void RadiationModel::addCategoryToCOCO(nlohmann::json &coco_json,
const std::vector<uint> &object_class_ID,
const std::vector<std::string> &category_name) {
1438 if (object_class_ID.size() != category_name.size()) {
1439 helios_runtime_error(
"ERROR (RadiationModel::addCategoryToCOCO): The lengths of object_class_ID and category_name vectors must be the same.");
1442 for (
size_t i = 0; i < object_class_ID.size(); ++i) {
1443 bool category_exists =
false;
1444 for (
auto &cat: coco_json[
"categories"]) {
1445 if (cat[
"id"] == object_class_ID[i]) {
1446 category_exists =
true;
1450 if (!category_exists) {
1451 nlohmann::json category;
1452 category[
"id"] = object_class_ID[i];
1453 category[
"name"] = category_name[i];
1454 category[
"supercategory"] =
"none";
1455 coco_json[
"categories"].push_back(category);
1461void RadiationModel::writeCOCOJson(
const nlohmann::json &coco_json,
const std::string &filename) {
1462 std::ofstream json_file(filename);
1463 if (!json_file.is_open()) {
1468 json_file << coco_json.dump(2) << std::endl;
1473std::map<int, std::vector<std::vector<bool>>> RadiationModel::generateLabelMasks(
const std::string &cameralabel,
const std::string &data_label,
bool use_object_data) {
1474 std::vector<uint> camera_UUIDs;
1475 std::string global_data_label =
"camera_" + cameralabel +
"_pixel_UUID";
1476 context->
getGlobalData(global_data_label.c_str(), camera_UUIDs);
1477 std::vector<uint> pixel_UUIDs = camera_UUIDs;
1478 int2 camera_resolution = cameras.at(cameralabel).resolution;
1480 std::map<int, std::vector<std::vector<bool>>> label_masks;
1483 for (
int j = 0; j < camera_resolution.
y; j++) {
1484 for (
int i = 0; i < camera_resolution.
x; i++) {
1485 uint ii = camera_resolution.
x - i - 1;
1486 uint UUID = pixel_UUIDs.at(j * camera_resolution.
x + ii) - 1;
1490 bool has_data =
false;
1492 if (use_object_data) {
1497 if (datatype == HELIOS_TYPE_UINT) {
1499 context->
getObjectData(objID, data_label.c_str(), labeldata_ui);
1500 labeldata = labeldata_ui;
1502 }
else if (datatype == HELIOS_TYPE_INT) {
1504 context->
getObjectData(objID, data_label.c_str(), labeldata_i);
1505 labeldata = (
uint) labeldata_i;
1513 if (datatype == HELIOS_TYPE_UINT) {
1516 labeldata = labeldata_ui;
1518 }
else if (datatype == HELIOS_TYPE_INT) {
1521 labeldata = (
uint) labeldata_i;
1529 if (label_masks.find(labeldata) == label_masks.end()) {
1530 label_masks[labeldata] = std::vector<std::vector<bool>>(camera_resolution.
y, std::vector<bool>(camera_resolution.
x,
false));
1532 label_masks[labeldata][j][i] =
true;
1542std::pair<int, int> RadiationModel::findStartingBoundaryPixel(
const std::vector<std::vector<bool>> &mask,
const helios::int2 &camera_resolution) {
1543 for (
int j = 0; j < camera_resolution.
y; j++) {
1544 for (
int i = 0; i < camera_resolution.
x; i++) {
1547 for (
int di = -1; di <= 1; di++) {
1548 for (
int dj = -1; dj <= 1; dj++) {
1549 if (di == 0 && dj == 0)
1553 if (ni < 0 || ni >= camera_resolution.
x || nj < 0 || nj >= camera_resolution.
y || !mask[nj][ni]) {
1565std::vector<std::pair<int, int>> RadiationModel::traceBoundaryMoore(
const std::vector<std::vector<bool>> &mask,
int start_x,
int start_y,
const helios::int2 &camera_resolution) {
1566 std::vector<std::pair<int, int>> contour;
1569 int dx[] = {1, 1, 0, -1, -1, -1, 0, 1};
1570 int dy[] = {0, 1, 1, 1, 0, -1, -1, -1};
1572 int x = start_x, y = start_y;
1576 contour.push_back({x, y});
1579 int start_dir = (dir + 6) % 8;
1582 for (
int i = 0; i < 8; i++) {
1583 int check_dir = (start_dir + i) % 8;
1584 int nx = x + dx[check_dir];
1585 int ny = y + dy[check_dir];
1588 if (nx >= 0 && nx < camera_resolution.x && ny >= 0 && ny < camera_resolution.
y && mask[ny][nx]) {
1600 }
while (!(x == start_x && y == start_y) && contour.size() < camera_resolution.
x * camera_resolution.
y);
1606std::vector<std::pair<int, int>> RadiationModel::traceBoundarySimple(
const std::vector<std::vector<bool>> &mask,
int start_x,
int start_y,
const helios::int2 &camera_resolution) {
1607 std::vector<std::pair<int, int>> contour;
1608 std::set<std::pair<int, int>> visited_boundary;
1611 std::queue<std::pair<int, int>> boundary_queue;
1612 boundary_queue.push({start_x, start_y});
1613 visited_boundary.insert({start_x, start_y});
1615 while (!boundary_queue.empty()) {
1616 auto [x, y] = boundary_queue.front();
1617 boundary_queue.pop();
1618 contour.push_back({x, y});
1621 for (
int di = -1; di <= 1; di++) {
1622 for (
int dj = -1; dj <= 1; dj++) {
1623 if (di == 0 && dj == 0)
1628 if (nx >= 0 && nx < camera_resolution.x && ny >= 0 && ny < camera_resolution.
y && mask[ny][nx] && visited_boundary.find({nx, ny}) == visited_boundary.end()) {
1631 bool is_boundary =
false;
1632 for (
int ddi = -1; ddi <= 1; ddi++) {
1633 for (
int ddj = -1; ddj <= 1; ddj++) {
1634 if (ddi == 0 && ddj == 0)
1638 if (nnx < 0 || nnx >= camera_resolution.
x || nny < 0 || nny >= camera_resolution.
y || !mask[nny][nnx]) {
1648 boundary_queue.push({nx, ny});
1649 visited_boundary.insert({nx, ny});
1660std::vector<std::map<std::string, std::vector<float>>> RadiationModel::generateAnnotationsFromMasks(
const std::map<
int, std::vector<std::vector<bool>>> &label_masks,
uint object_class_ID,
const helios::int2 &camera_resolution,
int image_id) {
1661 std::vector<std::map<std::string, std::vector<float>>> annotations;
1662 int annotation_id = 0;
1664 for (
const auto &label_pair: label_masks) {
1665 int label_value = label_pair.first;
1666 const auto &mask = label_pair.second;
1669 std::vector<std::vector<bool>> visited(camera_resolution.
y, std::vector<bool>(camera_resolution.
x,
false));
1672 for (
int j = 0; j < camera_resolution.
y; j++) {
1673 for (
int i = 0; i < camera_resolution.
x; i++) {
1674 if (mask[j][i] && !visited[j][i]) {
1676 int boundary_i = i, boundary_j = j;
1677 bool is_boundary =
false;
1680 for (
int di = -1; di <= 1; di++) {
1681 for (
int dj = -1; dj <= 1; dj++) {
1684 if (ni < 0 || ni >= camera_resolution.
x || nj < 0 || nj >= camera_resolution.
y || !mask[nj][ni]) {
1697 std::stack<std::pair<int, int>> stack;
1698 std::vector<std::pair<int, int>> component_pixels;
1700 visited[j][i] =
true;
1702 int min_x = i, max_x = i, min_y = j, max_y = j;
1705 while (!stack.empty()) {
1706 auto [ci, cj] = stack.top();
1709 component_pixels.push_back({ci, cj});
1711 min_x = std::min(min_x, ci);
1712 max_x = std::max(max_x, ci);
1713 min_y = std::min(min_y, cj);
1714 max_y = std::max(max_y, cj);
1717 for (
int di = -1; di <= 1; di++) {
1718 for (
int dj = -1; dj <= 1; dj++) {
1719 if (abs(di) + abs(dj) != 1)
1723 if (ni >= 0 && ni < camera_resolution.x && nj >= 0 && nj < camera_resolution.
y && mask[nj][ni] && !visited[nj][ni]) {
1724 stack.push({ni, nj});
1725 visited[nj][ni] =
true;
1732 auto start_pixel = findStartingBoundaryPixel(mask, camera_resolution);
1733 bool is_boundary_start =
false;
1735 if (start_pixel.first >= min_x && start_pixel.first <= max_x && start_pixel.second >= min_y && start_pixel.second <= max_y) {
1736 is_boundary_start =
true;
1739 if (is_boundary_start) {
1741 auto contour = traceBoundaryMoore(mask, start_pixel.first, start_pixel.second, camera_resolution);
1744 if (contour.size() < 10) {
1745 contour = traceBoundarySimple(mask, start_pixel.first, start_pixel.second, camera_resolution);
1748 if (contour.size() >= 3) {
1750 std::map<std::string, std::vector<float>> annotation;
1751 annotation[
"id"] = {(float) annotation_id++};
1752 annotation[
"image_id"] = {(float) image_id};
1753 annotation[
"category_id"] = {(float) object_class_ID};
1754 annotation[
"bbox"] = {(float) min_x, (
float) min_y, (float) (max_x - min_x), (float) (max_y - min_y)};
1755 annotation[
"area"] = {(float) area};
1756 annotation[
"iscrowd"] = {0.0f};
1759 std::vector<float> segmentation;
1760 for (
const auto &point: contour) {
1761 segmentation.push_back((
float) point.first);
1762 segmentation.push_back((
float) point.second);
1764 annotation[
"segmentation"] = segmentation;
1766 annotations.push_back(annotation);
1779 writeImageSegmentationMasks(cameralabel, std::vector<std::string>{primitive_data_label}, std::vector<uint>{object_class_ID}, json_filename, image_file, append_file);
1782void RadiationModel::writeImageSegmentationMasks(
const std::string &cameralabel,
const std::vector<std::string> &primitive_data_label,
const std::vector<uint> &object_class_ID,
const std::string &json_filename,
const std::string &image_file,
1785 if (cameras.find(cameralabel) == cameras.end()) {
1786 helios_runtime_error(
"ERROR (RadiationModel::writeImageSegmentationMasks): Camera '" + cameralabel +
"' does not exist.");
1789 if (primitive_data_label.size() != object_class_ID.size()) {
1790 helios_runtime_error(
"ERROR (RadiationModel::writeImageSegmentationMasks): The lengths of primitive_data_label and object_class_ID vectors must be the same.");
1794 std::string global_data_label =
"camera_" + cameralabel +
"_pixel_UUID";
1796 helios_runtime_error(
"ERROR (RadiationModel::writeImageSegmentationMasks): Pixel labels for camera '" + cameralabel +
"' do not exist. Was the radiation model run to generate labels?");
1801 for (
const auto &data_label: primitive_data_label) {
1802 if (std::find(all_primitive_data.begin(), all_primitive_data.end(), data_label) == all_primitive_data.end()) {
1803 std::cerr <<
"WARNING (RadiationModel::writeImageSegmentationMasks): Primitive data label '" << data_label <<
"' does not exist in the context." << std::endl;
1808 if (!std::filesystem::exists(image_file)) {
1809 helios_runtime_error(
"ERROR (RadiationModel::writeImageSegmentationMasks): Image file '" + image_file +
"' does not exist.");
1813 std::string validated_json_filename = json_filename;
1814 if (validated_json_filename.length() < 5 || validated_json_filename.substr(validated_json_filename.length() - 5) !=
".json") {
1815 validated_json_filename +=
".json";
1819 std::string outfile = validated_json_filename;
1822 int2 camera_resolution = cameras.at(cameralabel).resolution;
1823 auto coco_json_pair = initializeCOCOJsonWithImageId(outfile, append_file, cameralabel, camera_resolution, image_file);
1824 nlohmann::json coco_json = coco_json_pair.first;
1825 int image_id = coco_json_pair.second;
1826 addCategoryToCOCO(coco_json, object_class_ID, primitive_data_label);
1829 for (
size_t i = 0; i < primitive_data_label.size(); ++i) {
1831 std::map<int, std::vector<std::vector<bool>>> label_masks = generateLabelMasks(cameralabel, primitive_data_label[i],
false);
1834 std::vector<std::map<std::string, std::vector<float>>> annotations = generateAnnotationsFromMasks(label_masks, object_class_ID[i], camera_resolution, image_id);
1837 int max_annotation_id = -1;
1838 for (
const auto &existing_ann: coco_json[
"annotations"]) {
1839 if (existing_ann[
"id"] > max_annotation_id) {
1840 max_annotation_id = existing_ann[
"id"];
1845 for (
const auto &ann: annotations) {
1846 nlohmann::json json_annotation;
1847 json_annotation[
"id"] = max_annotation_id + 1;
1848 json_annotation[
"image_id"] = (int) ann.at(
"image_id")[0];
1849 json_annotation[
"category_id"] = (int) ann.at(
"category_id")[0];
1851 const auto &bbox = ann.at(
"bbox");
1852 json_annotation[
"bbox"] = {(int) bbox[0], (
int) bbox[1], (int) bbox[2], (
int) bbox[3]};
1853 json_annotation[
"area"] = (int) ann.at(
"area")[0];
1855 const auto &seg = ann.at(
"segmentation");
1856 std::vector<int> segmentation_coords;
1857 for (
float coord: seg) {
1858 segmentation_coords.push_back((
int) coord);
1860 json_annotation[
"segmentation"] = {segmentation_coords};
1861 json_annotation[
"iscrowd"] = (int) ann.at(
"iscrowd")[0];
1863 coco_json[
"annotations"].push_back(json_annotation);
1864 max_annotation_id++;
1869 writeCOCOJson(coco_json, outfile);
1879 if (cameras.find(cameralabel) == cameras.end()) {
1880 helios_runtime_error(
"ERROR (RadiationModel::writeImageSegmentationMasks_ObjectData): Camera '" + cameralabel +
"' does not exist.");
1883 if (object_data_label.size() != object_class_ID.size()) {
1884 helios_runtime_error(
"ERROR (RadiationModel::writeImageSegmentationMasks_ObjectData): The lengths of object_data_label and object_class_ID vectors must be the same.");
1888 std::string global_data_label =
"camera_" + cameralabel +
"_pixel_UUID";
1890 helios_runtime_error(
"ERROR (RadiationModel::writeImageSegmentationMasks_ObjectData): Pixel labels for camera '" + cameralabel +
"' do not exist. Was the radiation model run to generate labels?");
1895 for (
const auto &data_label: object_data_label) {
1896 if (std::find(all_object_data.begin(), all_object_data.end(), data_label) == all_object_data.end()) {
1897 std::cerr <<
"WARNING (RadiationModel::writeImageSegmentationMasks_ObjectData): Object data label '" << data_label <<
"' does not exist in the context." << std::endl;
1902 if (!std::filesystem::exists(image_file)) {
1903 helios_runtime_error(
"ERROR (RadiationModel::writeImageSegmentationMasks_ObjectData): Image file '" + image_file +
"' does not exist.");
1907 std::string validated_json_filename = json_filename;
1908 if (validated_json_filename.length() < 5 || validated_json_filename.substr(validated_json_filename.length() - 5) !=
".json") {
1909 validated_json_filename +=
".json";
1913 std::string outfile = validated_json_filename;
1916 int2 camera_resolution = cameras.at(cameralabel).resolution;
1917 auto coco_json_pair = initializeCOCOJsonWithImageId(outfile, append_file, cameralabel, camera_resolution, image_file);
1918 nlohmann::json coco_json = coco_json_pair.first;
1919 int image_id = coco_json_pair.second;
1920 addCategoryToCOCO(coco_json, object_class_ID, object_data_label);
1923 for (
size_t i = 0; i < object_data_label.size(); ++i) {
1925 std::map<int, std::vector<std::vector<bool>>> label_masks = generateLabelMasks(cameralabel, object_data_label[i],
true);
1928 std::vector<std::map<std::string, std::vector<float>>> annotations = generateAnnotationsFromMasks(label_masks, object_class_ID[i], camera_resolution, image_id);
1931 int max_annotation_id = -1;
1932 for (
const auto &existing_ann: coco_json[
"annotations"]) {
1933 if (existing_ann[
"id"] > max_annotation_id) {
1934 max_annotation_id = existing_ann[
"id"];
1939 for (
const auto &ann: annotations) {
1940 nlohmann::json json_annotation;
1941 json_annotation[
"id"] = max_annotation_id + 1;
1942 json_annotation[
"image_id"] = (int) ann.at(
"image_id")[0];
1943 json_annotation[
"category_id"] = (int) ann.at(
"category_id")[0];
1945 const auto &bbox = ann.at(
"bbox");
1946 json_annotation[
"bbox"] = {(int) bbox[0], (
int) bbox[1], (int) bbox[2], (
int) bbox[3]};
1947 json_annotation[
"area"] = (int) ann.at(
"area")[0];
1949 const auto &seg = ann.at(
"segmentation");
1950 std::vector<int> segmentation_coords;
1951 for (
float coord: seg) {
1952 segmentation_coords.push_back((
int) coord);
1954 json_annotation[
"segmentation"] = {segmentation_coords};
1955 json_annotation[
"iscrowd"] = (int) ann.at(
"iscrowd")[0];
1957 coco_json[
"annotations"].push_back(json_annotation);
1958 max_annotation_id++;
1963 writeCOCOJson(coco_json, outfile);
1967 for (
uint b = 0; b < bandlabels.size(); b++) {
1968 std::string bandlabel = bandlabels.at(b);
1970 std::string image_value_label =
"camera_" + cameralabel +
"_" + bandlabel;
1971 std::vector<float> cameradata;
1972 context->
getGlobalData(image_value_label.c_str(), cameradata);
1974 std::vector<uint> camera_UUIDs;
1975 std::string image_UUID_label =
"camera_" + cameralabel +
"_pixel_UUID";
1976 context->
getGlobalData(image_UUID_label.c_str(), camera_UUIDs);
1978 for (
uint i = 0; i < cameradata.size(); i++) {
1979 uint UUID = camera_UUIDs.at(i) - 1;
1981 cameradata.at(i) = padvalues.at(b);
1984 context->
setGlobalData(image_value_label.c_str(), cameradata);
1988void RadiationModel::calibrateCamera(
const std::string &originalcameralabel,
const std::vector<std::string> &sourcelabels,
const std::vector<std::string> &cameraresplabels_raw,
const std::vector<std::string> &bandlabels,
const float scalefactor,
1989 const std::vector<std::vector<float>> &truevalues,
const std::string &calibratedmark) {
1991 if (cameras.find(originalcameralabel) == cameras.end()) {
1992 helios_runtime_error(
"ERROR (RadiationModel::calibrateCamera): Camera " + originalcameralabel +
" does not exist.");
1993 }
else if (radiation_sources.empty()) {
1994 helios_runtime_error(
"ERROR (RadiationModel::calibrateCamera): No radiation sources were added to the radiation model. Cannot perform calibration.");
1998 if (!calibration_flag) {
1999 std::cout <<
"No color board added, use default color calibration." << std::endl;
2000 cameracalibration = &cameracalibration_;
2008 std::vector<std::string> cameraresplabels_cal(cameraresplabels_raw.size());
2010 for (
int iband = 0; iband < bandlabels.size(); iband++) {
2011 cameraresplabels_cal.at(iband) = calibratedmark +
"_" + cameraresplabels_raw.at(iband);
2020 std::cout <<
"Camera response scale: " << camerascale << std::endl;
2025void RadiationModel::calibrateCamera(
const std::string &originalcameralabel,
const float scalefactor,
const std::vector<std::vector<float>> &truevalues,
const std::string &calibratedmark) {
2027 if (cameras.find(originalcameralabel) == cameras.end()) {
2028 helios_runtime_error(
"ERROR (RadiationModel::calibrateCamera): Camera " + originalcameralabel +
" does not exist.");
2029 }
else if (radiation_sources.empty()) {
2030 helios_runtime_error(
"ERROR (RadiationModel::calibrateCamera): No radiation sources were added to the radiation model. Cannot perform calibration.");
2034 if (!calibration_flag) {
2035 std::cout <<
"No color board added, use default color calibration." << std::endl;
2039 RadiationModel::setCameraCalibration(&cameracalibration_);
2044 std::vector<std::string> bandlabels = cameras.at(originalcameralabel).band_labels;
2047 std::vector<std::string> cameraresplabels_cal(cameras.at(originalcameralabel).band_spectral_response.size());
2048 std::vector<std::string> cameraresplabels_raw = cameraresplabels_cal;
2051 for (
auto &band: cameras.at(originalcameralabel).band_spectral_response) {
2052 cameraresplabels_raw.at(iband) = band.second;
2053 cameraresplabels_cal.at(iband) = calibratedmark +
"_" + band.second;
2058 std::vector<std::string> sourcelabels(radiation_sources.size());
2060 for (
auto &source: radiation_sources) {
2061 if (source.source_spectrum.empty()) {
2062 helios_runtime_error(
"ERROR (RadiationModel::calibrateCamera): A spectral distribution was not specified for source " + source.source_spectrum_label +
". Cannot perform camera calibration.");
2064 sourcelabels.at(isource) = source.source_spectrum_label;
2075 std::cout <<
"Camera response scale: " << camerascale << std::endl;
2080std::vector<helios::vec2> RadiationModel::generateGaussianCameraResponse(
float FWHM,
float mu,
float centrawavelength,
const helios::int2 &wavebanrange) {
2083 float sigma = FWHM / (2 * std::sqrt(2 * std::log(2)));
2085 size_t lenspectra = wavebanrange.
y - wavebanrange.
x;
2086 std::vector<helios::vec2> cameraresponse(lenspectra);
2089 for (
int i = 0; i < lenspectra; ++i) {
2090 cameraresponse.at(i).x = float(wavebanrange.
x + i);
2094 for (
size_t i = 0; i < lenspectra; ++i) {
2095 cameraresponse.at(i).y = centrawavelength * std::exp(-std::pow((cameraresponse.at(i).x - mu), 2) / (2 * std::pow(sigma, 2)));
2099 return cameraresponse;
2102void RadiationModel::applyImageProcessingPipeline(
const std::string &cameralabel,
const std::string &red_band_label,
const std::string &green_band_label,
const std::string &blue_band_label,
float saturation_adjustment,
float brightness_adjustment,
2103 float contrast_adjustment,
float gain_adjustment) {
2105 if (cameras.find(cameralabel) == cameras.end()) {
2106 helios_runtime_error(
"ERROR (RadiationModel::applyImageProcessingPipeline): Camera '" + cameralabel +
"' does not exist.");
2109 if (camera.pixel_data.find(red_band_label) == camera.pixel_data.end() || camera.pixel_data.find(green_band_label) == camera.pixel_data.end() || camera.pixel_data.find(blue_band_label) == camera.pixel_data.end()) {
2110 helios_runtime_error(
"ERROR (RadiationModel::applyImageProcessingPipeline): One or more specified band labels do not exist for the camera pixel data.");
2114 camera.
autoExposure(red_band_label, green_band_label, blue_band_label, gain_adjustment);
2120 camera.
whiteBalanceAuto(red_band_label, green_band_label, blue_band_label);
2123 if (brightness_adjustment != 1.f || contrast_adjustment != 1.f) {
2124 camera.
adjustBrightnessContrast(red_band_label, green_band_label, blue_band_label, brightness_adjustment, contrast_adjustment);
2128 if (saturation_adjustment != 1.f) {
2129 camera.
adjustSaturation(red_band_label, green_band_label, blue_band_label, saturation_adjustment);
2133 camera.
gammaCompress(red_band_label, green_band_label, blue_band_label);
2138 float min_P = (std::numeric_limits<float>::max)();
2140 for (
const auto &[channel_label, data]: pixel_data) {
2141 for (
float v: data) {
2151 for (
auto &[channel_label, data]: pixel_data) {
2152 for (
float &v: data) {
2153 v = (v - min_P) / (max_P - min_P);
2161 if (pixel_data.find(red_band_label) == pixel_data.end() || pixel_data.find(green_band_label) == pixel_data.end() || pixel_data.find(blue_band_label) == pixel_data.end()) {
2162 helios_runtime_error(
"ERROR (RadiationModel::whiteBalance): One or more specified band labels do not exist for the camera pixel data.");
2166 auto &data_red = pixel_data.at(red_band_label);
2167 auto &data_green = pixel_data.at(green_band_label);
2168 auto &data_blue = pixel_data.at(blue_band_label);
2170 const std::size_t N = data_red.size();
2171 if (data_green.size() != N || data_blue.size() != N) {
2172 throw std::invalid_argument(
"All channels must have the same length");
2175 throw std::invalid_argument(
"Minkowski exponent p must satisfy p >= 1");
2182 float acc_r = 0.0f, acc_g = 0.0f, acc_b = 0.0f;
2183 for (std::size_t i = 0; i < N; ++i) {
2184 acc_r += std::pow(data_red[i], p);
2185 acc_g += std::pow(data_green[i], p);
2186 acc_b += std::pow(data_blue[i], p);
2188 float mean_r_p = acc_r /
static_cast<float>(N);
2189 float mean_g_p = acc_g /
static_cast<float>(N);
2190 float mean_b_p = acc_b /
static_cast<float>(N);
2192 float M_R = std::pow(mean_r_p, 1.0f / p);
2193 float M_G = std::pow(mean_g_p, 1.0f / p);
2194 float M_B = std::pow(mean_b_p, 1.0f / p);
2197 const float eps = 1e-6f;
2198 if (M_R < eps || M_G < eps || M_B < eps) {
2199 throw std::runtime_error(
"Channel Minkowski mean too small");
2204 float M = (M_R + M_G + M_B) / 3.0f;
2215 for (std::size_t i = 0; i < N; ++i) {
2216 data_red[i] *= scale.
x;
2217 data_green[i] *= scale.
y;
2218 data_blue[i] *= scale.
z;
2225 if (pixel_data.find(red_band_label) == pixel_data.end() || pixel_data.find(green_band_label) == pixel_data.end() || pixel_data.find(blue_band_label) == pixel_data.end()) {
2226 helios_runtime_error(
"ERROR (RadiationModel::whiteBalanceGrayEdge): One or more specified band labels do not exist for the camera pixel data.");
2230 auto &data_red = pixel_data.at(red_band_label);
2231 auto &data_green = pixel_data.at(green_band_label);
2232 auto &data_blue = pixel_data.at(blue_band_label);
2234 const int width = resolution.
x;
2235 const int height = resolution.
y;
2236 const std::size_t N = width * height;
2239 throw std::invalid_argument(
"Minkowski exponent p must satisfy p >= 1");
2241 if (derivative_order < 1 || derivative_order > 2) {
2242 throw std::invalid_argument(
"Derivative order must be 1 or 2");
2246 std::vector<float> deriv_red(N, 0.0f);
2247 std::vector<float> deriv_green(N, 0.0f);
2248 std::vector<float> deriv_blue(N, 0.0f);
2250 if (derivative_order == 1) {
2252 for (
int y = 1; y < height - 1; ++y) {
2253 for (
int x = 1; x < width - 1; ++x) {
2254 int idx = y * width + x;
2257 float dx_r = (data_red[(y - 1) * width + (x + 1)] + 2 * data_red[y * width + (x + 1)] + data_red[(y + 1) * width + (x + 1)]) -
2258 (data_red[(y - 1) * width + (x - 1)] + 2 * data_red[y * width + (x - 1)] + data_red[(y + 1) * width + (x - 1)]) / 8.0f;
2259 float dy_r = (data_red[(y + 1) * width + (x - 1)] + 2 * data_red[(y + 1) * width + x] + data_red[(y + 1) * width + (x + 1)]) -
2260 (data_red[(y - 1) * width + (x - 1)] + 2 * data_red[(y - 1) * width + x] + data_red[(y - 1) * width + (x + 1)]) / 8.0f;
2261 deriv_red[idx] = std::sqrt(dx_r * dx_r + dy_r * dy_r);
2263 float dx_g = (data_green[(y - 1) * width + (x + 1)] + 2 * data_green[y * width + (x + 1)] + data_green[(y + 1) * width + (x + 1)]) -
2264 (data_green[(y - 1) * width + (x - 1)] + 2 * data_green[y * width + (x - 1)] + data_green[(y + 1) * width + (x - 1)]) / 8.0f;
2265 float dy_g = (data_green[(y + 1) * width + (x - 1)] + 2 * data_green[(y + 1) * width + x] + data_green[(y + 1) * width + (x + 1)]) -
2266 (data_green[(y - 1) * width + (x - 1)] + 2 * data_green[(y - 1) * width + x] + data_green[(y - 1) * width + (x + 1)]) / 8.0f;
2267 deriv_green[idx] = std::sqrt(dx_g * dx_g + dy_g * dy_g);
2269 float dx_b = (data_blue[(y - 1) * width + (x + 1)] + 2 * data_blue[y * width + (x + 1)] + data_blue[(y + 1) * width + (x + 1)]) -
2270 (data_blue[(y - 1) * width + (x - 1)] + 2 * data_blue[y * width + (x - 1)] + data_blue[(y + 1) * width + (x - 1)]) / 8.0f;
2271 float dy_b = (data_blue[(y + 1) * width + (x - 1)] + 2 * data_blue[(y + 1) * width + x] + data_blue[(y + 1) * width + (x + 1)]) -
2272 (data_blue[(y - 1) * width + (x - 1)] + 2 * data_blue[(y - 1) * width + x] + data_blue[(y - 1) * width + (x + 1)]) / 8.0f;
2273 deriv_blue[idx] = std::sqrt(dx_b * dx_b + dy_b * dy_b);
2278 for (
int y = 1; y < height - 1; ++y) {
2279 for (
int x = 1; x < width - 1; ++x) {
2280 int idx = y * width + x;
2282 deriv_red[idx] = std::abs(data_red[(y - 1) * width + x] + data_red[(y + 1) * width + x] + data_red[y * width + (x - 1)] + data_red[y * width + (x + 1)] - 4 * data_red[idx]);
2284 deriv_green[idx] = std::abs(data_green[(y - 1) * width + x] + data_green[(y + 1) * width + x] + data_green[y * width + (x - 1)] + data_green[y * width + (x + 1)] - 4 * data_green[idx]);
2286 deriv_blue[idx] = std::abs(data_blue[(y - 1) * width + x] + data_blue[(y + 1) * width + x] + data_blue[y * width + (x - 1)] + data_blue[y * width + (x + 1)] - 4 * data_blue[idx]);
2292 float acc_r = 0.0f, acc_g = 0.0f, acc_b = 0.0f;
2293 int valid_pixels = 0;
2295 for (std::size_t i = 0; i < N; ++i) {
2296 if (deriv_red[i] > 0 || deriv_green[i] > 0 || deriv_blue[i] > 0) {
2297 acc_r += std::pow(deriv_red[i], p);
2298 acc_g += std::pow(deriv_green[i], p);
2299 acc_b += std::pow(deriv_blue[i], p);
2304 if (valid_pixels == 0) {
2306 whiteBalance(red_band_label, green_band_label, blue_band_label, p);
2310 float mean_r_p = acc_r /
static_cast<float>(valid_pixels);
2311 float mean_g_p = acc_g /
static_cast<float>(valid_pixels);
2312 float mean_b_p = acc_b /
static_cast<float>(valid_pixels);
2314 float M_R = std::pow(mean_r_p, 1.0f / p);
2315 float M_G = std::pow(mean_g_p, 1.0f / p);
2316 float M_B = std::pow(mean_b_p, 1.0f / p);
2319 const float eps = 1e-6f;
2320 if (M_R < eps || M_G < eps || M_B < eps) {
2322 whiteBalance(red_band_label, green_band_label, blue_band_label, p);
2327 float M = (M_R + M_G + M_B) / 3.0f;
2336 for (std::size_t i = 0; i < N; ++i) {
2337 data_red[i] *= scale.
x;
2338 data_green[i] *= scale.
y;
2339 data_blue[i] *= scale.
z;
2346 if (pixel_data.find(red_band_label) == pixel_data.end() || pixel_data.find(green_band_label) == pixel_data.end() || pixel_data.find(blue_band_label) == pixel_data.end()) {
2347 helios_runtime_error(
"ERROR (RadiationModel::whiteBalanceWhitePatch): One or more specified band labels do not exist for the camera pixel data.");
2351 if (percentile <= 0.0f || percentile > 1.0f) {
2352 throw std::invalid_argument(
"Percentile must be in range (0, 1]");
2355 auto &data_red = pixel_data.at(red_band_label);
2356 auto &data_green = pixel_data.at(green_band_label);
2357 auto &data_blue = pixel_data.at(blue_band_label);
2359 const std::size_t N = data_red.size();
2362 std::vector<float> sorted_red = data_red;
2363 std::vector<float> sorted_green = data_green;
2364 std::vector<float> sorted_blue = data_blue;
2366 std::size_t k =
static_cast<std::size_t
>(percentile * (N - 1));
2368 std::nth_element(sorted_red.begin(), sorted_red.begin() + k, sorted_red.end());
2369 std::nth_element(sorted_green.begin(), sorted_green.begin() + k, sorted_green.end());
2370 std::nth_element(sorted_blue.begin(), sorted_blue.begin() + k, sorted_blue.end());
2372 float white_r = sorted_red[k];
2373 float white_g = sorted_green[k];
2374 float white_b = sorted_blue[k];
2377 const float eps = 1e-6f;
2378 if (white_r < eps || white_g < eps || white_b < eps) {
2379 throw std::runtime_error(
"White patch values too small");
2383 for (std::size_t i = 0; i < N; ++i) {
2384 data_red[i] /= white_r;
2385 data_green[i] /= white_g;
2386 data_blue[i] /= white_b;
2393 if (pixel_data.find(red_band_label) == pixel_data.end() || pixel_data.find(green_band_label) == pixel_data.end() || pixel_data.find(blue_band_label) == pixel_data.end()) {
2394 helios_runtime_error(
"ERROR (RadiationModel::whiteBalanceAuto): One or more specified band labels do not exist for the camera pixel data.");
2398 const auto &data_red = pixel_data.at(red_band_label);
2399 const auto &data_green = pixel_data.at(green_band_label);
2400 const auto &data_blue = pixel_data.at(blue_band_label);
2402 const std::size_t N = data_red.size();
2403 const int width = resolution.
x;
2404 const int height = resolution.
y;
2409 float mean_r = 0.0f, mean_g = 0.0f, mean_b = 0.0f;
2410 for (std::size_t i = 0; i < N; ++i) {
2411 mean_r += data_red[i];
2412 mean_g += data_green[i];
2413 mean_b += data_blue[i];
2419 float green_dominance = mean_g / (mean_r + mean_g + mean_b + 1e-6f);
2422 float bright_threshold = 0.9f;
2423 int bright_pixels = 0;
2424 for (std::size_t i = 0; i < N; ++i) {
2425 float max_val = std::max({data_red[i], data_green[i], data_blue[i]});
2426 if (max_val > bright_threshold) {
2430 float bright_ratio =
static_cast<float>(bright_pixels) / N;
2433 float edge_density = 0.0f;
2434 for (
int y = 1; y < height - 1; ++y) {
2435 for (
int x = 1; x < width - 1; ++x) {
2436 int idx = y * width + x;
2439 float dx = std::abs(data_green[idx + 1] - data_green[idx - 1]);
2440 float dy = std::abs(data_green[idx + width] - data_green[idx - width]);
2441 edge_density += std::sqrt(dx * dx + dy * dy);
2444 edge_density /= ((width - 2) * (height - 2));
2456 if (pixel_data.find(red_band_label) == pixel_data.end() || pixel_data.find(green_band_label) == pixel_data.end() || pixel_data.find(blue_band_label) == pixel_data.end()) {
2457 helios_runtime_error(
"ERROR (RadiationCamera::applyCameraSpectralCorrection): One or more specified band labels do not exist for the camera pixel data.");
2462 if (band_spectral_response.find(red_band_label) == band_spectral_response.end() || band_spectral_response.find(green_band_label) == band_spectral_response.end() || band_spectral_response.find(blue_band_label) == band_spectral_response.end()) {
2467 std::string red_response_id = band_spectral_response.at(red_band_label);
2468 std::string green_response_id = band_spectral_response.at(green_band_label);
2469 std::string blue_response_id = band_spectral_response.at(blue_band_label);
2472 if (red_response_id ==
"uniform" && green_response_id ==
"uniform" && blue_response_id ==
"uniform") {
2478 std::vector<helios::vec2> red_spectrum, green_spectrum, blue_spectrum;
2480 if (red_response_id !=
"uniform" && context->
doesGlobalDataExist(red_response_id.c_str())) {
2481 context->
getGlobalData(red_response_id.c_str(), red_spectrum);
2483 if (green_response_id !=
"uniform" && context->
doesGlobalDataExist(green_response_id.c_str())) {
2484 context->
getGlobalData(green_response_id.c_str(), green_spectrum);
2486 if (blue_response_id !=
"uniform" && context->
doesGlobalDataExist(blue_response_id.c_str())) {
2487 context->
getGlobalData(blue_response_id.c_str(), blue_spectrum);
2491 if (red_spectrum.empty() || green_spectrum.empty() || blue_spectrum.empty()) {
2497 float red_integrated = 0.0f, green_integrated = 0.0f, blue_integrated = 0.0f;
2500 for (
size_t i = 1; i < red_spectrum.size(); ++i) {
2501 float dw = red_spectrum[i].x - red_spectrum[i - 1].x;
2502 red_integrated += 0.5f * (red_spectrum[i].y + red_spectrum[i - 1].y) * dw;
2504 for (
size_t i = 1; i < green_spectrum.size(); ++i) {
2505 float dw = green_spectrum[i].x - green_spectrum[i - 1].x;
2506 green_integrated += 0.5f * (green_spectrum[i].y + green_spectrum[i - 1].y) * dw;
2508 for (
size_t i = 1; i < blue_spectrum.size(); ++i) {
2509 float dw = blue_spectrum[i].x - blue_spectrum[i - 1].x;
2510 blue_integrated += 0.5f * (blue_spectrum[i].y + blue_spectrum[i - 1].y) * dw;
2514 if (red_integrated <= 0 || green_integrated <= 0 || blue_integrated <= 0) {
2520 float reference_response = std::min({red_integrated, green_integrated, blue_integrated});
2523 correction_factors.
x = reference_response / red_integrated;
2524 correction_factors.y = reference_response / green_integrated;
2525 correction_factors.z = reference_response / blue_integrated;
2528 auto &data_red = pixel_data.at(red_band_label);
2529 auto &data_green = pixel_data.at(green_band_label);
2530 auto &data_blue = pixel_data.at(blue_band_label);
2532 const std::size_t N = data_red.size();
2533 for (std::size_t i = 0; i < N; ++i) {
2534 data_red[i] *= correction_factors.x;
2535 data_green[i] *= correction_factors.y;
2536 data_blue[i] *= correction_factors.z;
2539 }
catch (
const std::exception &e) {
2547 if (pixel_data.find(red_band_label) == pixel_data.end() || pixel_data.find(green_band_label) == pixel_data.end() || pixel_data.find(blue_band_label) == pixel_data.end()) {
2548 helios_runtime_error(
"ERROR (RadiationModel::reinhardToneMapping): One or more specified band labels do not exist for the camera pixel data.");
2552 const std::size_t N = resolution.
x * resolution.
y;
2553 constexpr float eps = 1e-6f;
2555 auto &data_red = pixel_data.at(red_band_label);
2556 auto &data_green = pixel_data.at(green_band_label);
2557 auto &data_blue = pixel_data.at(blue_band_label);
2558 for (std::size_t i = 0; i < N; ++i) {
2559 float R = data_red[i], G = data_green[i], B = data_blue[i];
2560 float L = luminance(
R, G, B);
2561 float s = (L > eps) ? (L / (1.0f + L)) / L : 0.0f;
2563 data_red[i] =
R * s;
2564 data_green[i] = G * s;
2565 data_blue[i] = B * s;
2569void RadiationCamera::applyGain(
const std::string &red_band_label,
const std::string &green_band_label,
const std::string &blue_band_label,
float percentile) {
2572 if (pixel_data.find(red_band_label) == pixel_data.end() || pixel_data.find(green_band_label) == pixel_data.end() || pixel_data.find(blue_band_label) == pixel_data.end()) {
2573 helios_runtime_error(
"ERROR (RadiationModel::applyGain): One or more specified band labels do not exist for the camera pixel data.");
2577 const std::size_t N = resolution.
x * resolution.
y;
2579 auto &data_red = pixel_data.at(red_band_label);
2580 auto &data_green = pixel_data.at(green_band_label);
2581 auto &data_blue = pixel_data.at(blue_band_label);
2583 std::vector<float> luminance_pixel;
2584 luminance_pixel.reserve(N);
2585 for (std::size_t i = 0; i < N; ++i) {
2586 luminance_pixel.push_back(luminance(data_red[i], data_green[i], data_blue[i]));
2589 std::size_t k = std::size_t(percentile * (luminance_pixel.size() - 1));
2590 std::nth_element(luminance_pixel.begin(), luminance_pixel.begin() + k, luminance_pixel.end());
2591 float peak = luminance_pixel[k];
2592 float gain = (peak > 0.0f) ? 1.0f / peak : 1.0f;
2594 for (
auto &[channel, data]: pixel_data) {
2595 for (
float &v: data) {
2604 if (pixel_data.find(red_band_label) == pixel_data.end() || pixel_data.find(green_band_label) == pixel_data.end() || pixel_data.find(blue_band_label) == pixel_data.end()) {
2605 helios_runtime_error(
"ERROR (RadiationModel::globalHistogramEquilization): One or more specified band labels do not exist for the camera pixel data.");
2609 const size_t N = resolution.
x * resolution.
y;
2610 const float eps = 1e-6f;
2612 auto &data_red = pixel_data.at(red_band_label);
2613 auto &data_green = pixel_data.at(green_band_label);
2614 auto &data_blue = pixel_data.at(blue_band_label);
2617 std::vector<float> lum(N);
2618 std::vector<float> chroma_r(N), chroma_g(N), chroma_b(N);
2620 for (
size_t i = 0; i < N; ++i) {
2621 vec3 p(data_red[i], data_green[i], data_blue[i]);
2622 lum[i] = 0.2126f * p.
x + 0.7152f * p.
y + 0.0722f * p.
z;
2626 chroma_r[i] = p.
x / lum[i];
2627 chroma_g[i] = p.
y / lum[i];
2628 chroma_b[i] = p.
z / lum[i];
2638 std::vector<int> hist(B, 0);
2639 for (
float v: lum) {
2640 int b = int(std::clamp(v, 0.0f, 1.0f - eps) * B);
2641 if (b >= 0 && b < 2048) {
2645 std::vector<float> cdf(B);
2647 for (
int b = 0; b < B; ++b) {
2649 cdf[b] = float(acc) / float(N);
2653 for (
size_t i = 0; i < N; ++i) {
2655 if (lum[i] >= 1.0f) {
2656 data_red[i] = std::min(1.0f, data_red[i]);
2657 data_green[i] = std::min(1.0f, data_green[i]);
2658 data_blue[i] = std::min(1.0f, data_blue[i]);
2662 int b = int(std::clamp(lum[i], 0.0f, 1.0f - eps) * B);
2664 if (b < 0 || b >= 2048) {
2668 constexpr float k = 0.2f;
2669 constexpr float cs = 0.2f;
2672 float Ynew = (1.0f - k) * lum[i] + k * Yeq;
2675 float t = Ynew - 0.5f;
2676 Ynew = 0.5f + t * (1.0f + cs - 2.0f * cs * std::fabs(t));
2679 data_red[i] = Ynew * chroma_r[i];
2680 data_green[i] = Ynew * chroma_g[i];
2681 data_blue[i] = Ynew * chroma_b[i];
2685void RadiationCamera::adjustSBC(
const std::string &red_band_label,
const std::string &green_band_label,
const std::string &blue_band_label,
float saturation,
float brightness,
float contrast) {
2687 if (pixel_data.find(red_band_label) == pixel_data.end() || pixel_data.find(green_band_label) == pixel_data.end() || pixel_data.find(blue_band_label) == pixel_data.end()) {
2688 helios_runtime_error(
"ERROR (RadiationModel::adjustSBC): One or more specified band labels do not exist for the camera pixel data.");
2692 constexpr float kRedW = 0.2126f;
2693 constexpr float kGreenW = 0.7152f;
2694 constexpr float kBlueW = 0.0722f;
2696 const size_t N = resolution.
x * resolution.
y;
2698 auto &data_red = pixel_data.at(red_band_label);
2699 auto &data_green = pixel_data.at(green_band_label);
2700 auto &data_blue = pixel_data.at(blue_band_label);
2702 for (
int i = 0; i < N; ++i) {
2704 helios::vec3 p(data_red[i], data_green[i], data_blue[i]);
2707 float Y = kRedW * p.
x + kGreenW * p.
y + kBlueW * p.
z;
2719 data_red[i] =
clamp(p.
x, 0.0f, 1.0f);
2720 data_green[i] =
clamp(p.
y, 0.0f, 1.0f);
2721 data_blue[i] =
clamp(p.
z, 0.0f, 1.0f);
2742 if (pixel_data.find(red_band_label) == pixel_data.end() || pixel_data.find(green_band_label) == pixel_data.end() || pixel_data.find(blue_band_label) == pixel_data.end()) {
2743 helios_runtime_error(
"ERROR (RadiationModel::gammaCompress): One or more specified band labels do not exist for the camera pixel data.");
2747 for (
float &v: pixel_data.at(red_band_label)) {
2748 v = lin_to_srgb(std::fmaxf(0.0f, v));
2750 for (
float &v: pixel_data.at(green_band_label)) {
2751 v = lin_to_srgb(std::fmaxf(0.0f, v));
2753 for (
float &v: pixel_data.at(blue_band_label)) {
2754 v = lin_to_srgb(std::fmaxf(0.0f, v));
2760void RadiationCamera::autoExposure(
const std::string &red_band_label,
const std::string &green_band_label,
const std::string &blue_band_label,
float gain_multiplier) {
2762 if (pixel_data.find(red_band_label) == pixel_data.end() || pixel_data.find(green_band_label) == pixel_data.end() || pixel_data.find(blue_band_label) == pixel_data.end()) {
2763 helios_runtime_error(
"ERROR (RadiationModel::autoExposure): One or more specified band labels do not exist for the camera pixel data.");
2767 auto &data_red = pixel_data.at(red_band_label);
2768 auto &data_green = pixel_data.at(green_band_label);
2769 auto &data_blue = pixel_data.at(blue_band_label);
2771 const std::size_t N = data_red.size();
2774 std::vector<float> luminance_values(N);
2775 for (std::size_t i = 0; i < N; ++i) {
2776 luminance_values[i] = luminance(data_red[i], data_green[i], data_blue[i]);
2780 std::vector<float> sorted_luminance = luminance_values;
2781 std::sort(sorted_luminance.begin(), sorted_luminance.end());
2784 std::size_t p95_idx =
static_cast<std::size_t
>(0.95f * (N - 1));
2785 float p95_luminance = sorted_luminance[p95_idx];
2788 std::size_t median_idx = N / 2;
2789 float median_luminance = sorted_luminance[median_idx];
2793 float target_median = 0.18f;
2794 float auto_gain = target_median / std::max(median_luminance, 1e-6f);
2800 float final_gain = auto_gain * gain_multiplier;
2803 for (std::size_t i = 0; i < N; ++i) {
2804 data_red[i] *= final_gain;
2805 data_green[i] *= final_gain;
2806 data_blue[i] *= final_gain;
2812 if (pixel_data.find(red_band_label) == pixel_data.end() || pixel_data.find(green_band_label) == pixel_data.end() || pixel_data.find(blue_band_label) == pixel_data.end()) {
2813 helios_runtime_error(
"ERROR (RadiationModel::adjustBrightnessContrast): One or more specified band labels do not exist for the camera pixel data.");
2817 auto &data_red = pixel_data.at(red_band_label);
2818 auto &data_green = pixel_data.at(green_band_label);
2819 auto &data_blue = pixel_data.at(blue_band_label);
2821 const std::size_t N = data_red.size();
2823 for (std::size_t i = 0; i < N; ++i) {
2825 float r = data_red[i] * brightness;
2826 float g = data_green[i] * brightness;
2827 float b = data_blue[i] * brightness;
2830 r = 0.5f + (r - 0.5f) * contrast;
2831 g = 0.5f + (g - 0.5f) * contrast;
2832 b = 0.5f + (b - 0.5f) * contrast;
2843 if (pixel_data.find(red_band_label) == pixel_data.end() || pixel_data.find(green_band_label) == pixel_data.end() || pixel_data.find(blue_band_label) == pixel_data.end()) {
2844 helios_runtime_error(
"ERROR (RadiationModel::adjustSaturation): One or more specified band labels do not exist for the camera pixel data.");
2848 auto &data_red = pixel_data.at(red_band_label);
2849 auto &data_green = pixel_data.at(green_band_label);
2850 auto &data_blue = pixel_data.at(blue_band_label);
2852 const std::size_t N = data_red.size();
2854 for (std::size_t i = 0; i < N; ++i) {
2855 float r = data_red[i];
2856 float g = data_green[i];
2857 float b = data_blue[i];
2860 float lum = luminance(r, g, b);
2863 data_red[i] = lum + saturation * (r - lum);
2864 data_green[i] = lum + saturation * (g - lum);
2865 data_blue[i] = lum + saturation * (b - lum);