ROS2ImageEncodingConversionComponent.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "ROS2ImageEncodingConversionComponent.h"
  9. #include <AzCore/Outcome/Outcome.h>
  10. #include <AzCore/RTTI/RTTIMacros.h>
  11. #include <AzCore/Serialization/EditContext.h>
  12. #include <AzCore/Serialization/SerializeContext.h>
  13. #include <sensor_msgs/msg/detail/image__struct.hpp>
  14. namespace AZStd
  15. {
  16. template<>
  17. struct hash<ROS2::EncodingConversion>
  18. {
  19. size_t operator()(const ROS2::EncodingConversion& data) const
  20. {
  21. return (static_cast<AZ::u16>(data.encodingIn) << 8) | static_cast<AZ::u16>(data.encodingOut);
  22. }
  23. };
  24. } // namespace AZStd
  25. namespace ROS2
  26. {
  27. namespace
  28. {
  29. const AZStd::unordered_map<ImageEncoding, const char*> ImageEncodingNames = {
  30. { ImageEncoding::RGBA8, "rgba8" },
  31. { ImageEncoding::RGB8, "rgb8" },
  32. { ImageEncoding::Mono8, "mono8" },
  33. { ImageEncoding::Mono16, "mono16" },
  34. };
  35. const AZStd::unordered_map<AZStd::string, ImageEncoding> ImageEncodingFromName = {
  36. { "rgba8", ImageEncoding::RGBA8 },
  37. { "rgb8", ImageEncoding::RGB8 },
  38. { "mono8", ImageEncoding::Mono8 },
  39. { "mono16", ImageEncoding::Mono16 },
  40. };
  41. void Rgba8ToRgb8(sensor_msgs::msg::Image& image)
  42. {
  43. const std::string inputEncoding = ImageEncodingNames.at(ImageEncoding::RGBA8);
  44. const std::string outputEncoding = ImageEncodingNames.at(ImageEncoding::RGB8);
  45. AZ_Assert(image.encoding == inputEncoding, "Image encoding is %s, expected %s", image.encoding.c_str(), inputEncoding.c_str());
  46. AZ_Assert(image.step == image.width * 4, "Image step (%d) is not width * 4 (%d)", image.step, image.width * 4);
  47. AZ_Assert(
  48. image.data.size() == image.step * image.height,
  49. "Image data size (%d) is not step * height (%d)",
  50. image.data.size(),
  51. image.step * image.height);
  52. // Perform conversion in place
  53. for (size_t pixelId = 0; pixelId < image.width * image.height; ++pixelId)
  54. {
  55. size_t pixelOffsetIn = pixelId * 4;
  56. size_t pixelOffsetOut = pixelId * 3;
  57. image.data[pixelOffsetOut] = image.data[pixelOffsetIn];
  58. image.data[pixelOffsetOut + 1] = image.data[pixelOffsetIn + 1];
  59. image.data[pixelOffsetOut + 2] = image.data[pixelOffsetIn + 2];
  60. }
  61. image.encoding = outputEncoding;
  62. image.step = image.width * 3;
  63. image.data.resize(image.step * image.height);
  64. }
  65. const AZStd::unordered_map<EncodingConversion, const AZStd::function<void(sensor_msgs::msg::Image&)>> supportedFormatChange = {
  66. { { ImageEncoding::RGBA8, ImageEncoding::RGB8 }, Rgba8ToRgb8 },
  67. };
  68. AZ::Outcome<void, AZStd::string> ValidateEncodingConversion(EncodingConversion newConversion)
  69. {
  70. if (newConversion.encodingIn == newConversion.encodingOut)
  71. {
  72. return AZ::Failure(AZStd::string("Conversion to same type is forbidden"));
  73. }
  74. if (supportedFormatChange.find(newConversion) == supportedFormatChange.end())
  75. {
  76. return AZ::Failure(AZStd::string::format(
  77. "Unsupported encoding change from %s to %s",
  78. ImageEncodingNames.at(newConversion.encodingIn),
  79. ImageEncodingNames.at(newConversion.encodingOut)));
  80. }
  81. return AZ::Success();
  82. }
  83. } // namespace
  84. void EncodingConversion::Reflect(AZ::ReflectContext* context)
  85. {
  86. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  87. {
  88. serializeContext->Class<EncodingConversion>()
  89. ->Version(0)
  90. ->Field("EncodingIn", &EncodingConversion::encodingIn)
  91. ->Field("EncodingOut", &EncodingConversion::encodingOut);
  92. if (AZ::EditContext* ec = serializeContext->GetEditContext())
  93. {
  94. ec->Class<EncodingConversion>("Encoding Conversion", "Specifies encoding conversion")
  95. ->DataElement(
  96. AZ::Edit::UIHandlers::ComboBox, &EncodingConversion::encodingIn, "Encoding In", "Encoding of the input image")
  97. ->EnumAttribute(ImageEncoding::RGBA8, "rgba8")
  98. ->EnumAttribute(ImageEncoding::RGB8, "rgb8")
  99. ->EnumAttribute(ImageEncoding::Mono8, "mono8")
  100. ->EnumAttribute(ImageEncoding::Mono16, "mono16")
  101. ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EncodingConversion::ValidateInputEncoding)
  102. ->DataElement(
  103. AZ::Edit::UIHandlers::ComboBox, &EncodingConversion::encodingOut, "Encoding Out", "Encoding of the output image")
  104. ->EnumAttribute(ImageEncoding::RGBA8, "rgba8")
  105. ->EnumAttribute(ImageEncoding::RGB8, "rgb8")
  106. ->EnumAttribute(ImageEncoding::Mono8, "mono8")
  107. ->EnumAttribute(ImageEncoding::Mono16, "mono16")
  108. ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EncodingConversion::ValidateOutputEncoding);
  109. }
  110. }
  111. }
  112. AZ::Outcome<void, AZStd::string> EncodingConversion::ValidateInputEncoding(void* newValue, const AZ::Uuid& valueType)
  113. {
  114. ImageEncoding* newEncoding = static_cast<ImageEncoding*>(newValue);
  115. return ValidateEncodingConversion({ *newEncoding, encodingOut });
  116. }
  117. AZ::Outcome<void, AZStd::string> EncodingConversion::ValidateOutputEncoding(void* newValue, const AZ::Uuid& valueType)
  118. {
  119. ImageEncoding* newEncoding = static_cast<ImageEncoding*>(newValue);
  120. return ValidateEncodingConversion({ encodingIn, *newEncoding });
  121. }
  122. void ROS2ImageEncodingConversionComponent::Reflect(AZ::ReflectContext* context)
  123. {
  124. EncodingConversion::Reflect(context);
  125. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  126. {
  127. serializeContext->Class<ROS2ImageEncodingConversionComponent, AZ::Component>()
  128. ->Version(0)
  129. ->Field("Priority", &ROS2ImageEncodingConversionComponent::m_priority)
  130. ->Field("EncodingConvertData", &ROS2ImageEncodingConversionComponent::m_encodingConvertData);
  131. if (AZ::EditContext* ec = serializeContext->GetEditContext())
  132. {
  133. ec->Class<ROS2ImageEncodingConversionComponent>(
  134. "Image Encoding Conversion Component", "Converts image encoding to a different encoding")
  135. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  136. ->Attribute(AZ::Edit::Attributes::Category, "ROS2")
  137. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"))
  138. ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/ROS2CameraSensor.svg")
  139. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/ROS2CameraSensor.svg")
  140. ->DataElement(
  141. AZ::Edit::UIHandlers::Default,
  142. &ROS2ImageEncodingConversionComponent::m_priority,
  143. "Priority",
  144. "Priority of the post processing. The higher the number the later the post processing is applied.")
  145. ->Attribute(AZ::Edit::Attributes::Min, CameraPostProcessingRequests::MIN_PRIORITY)
  146. ->Attribute(AZ::Edit::Attributes::Max, CameraPostProcessingRequests::MAX_PRIORITY)
  147. ->DataElement(
  148. AZ::Edit::UIHandlers::Default,
  149. &ROS2ImageEncodingConversionComponent::m_encodingConvertData,
  150. "Encoding Conversion",
  151. "Specifies the encoding conversion to apply");
  152. }
  153. }
  154. }
  155. void ROS2ImageEncodingConversionComponent::Activate()
  156. {
  157. CameraPostProcessingRequestBus::Handler::BusConnect(GetEntityId());
  158. }
  159. void ROS2ImageEncodingConversionComponent::Deactivate()
  160. {
  161. CameraPostProcessingRequestBus::Handler::BusDisconnect();
  162. }
  163. void ROS2ImageEncodingConversionComponent::ApplyPostProcessing(sensor_msgs::msg::Image& image)
  164. {
  165. const auto nameIter = ImageEncodingFromName.find(image.encoding.c_str());
  166. if (nameIter == ImageEncodingFromName.end())
  167. {
  168. return;
  169. }
  170. const ImageEncoding& encoding = nameIter->second;
  171. if (encoding != m_encodingConvertData.encodingIn)
  172. {
  173. return;
  174. }
  175. const auto convertIter = supportedFormatChange.find(m_encodingConvertData);
  176. if (convertIter == supportedFormatChange.end())
  177. {
  178. return;
  179. }
  180. convertIter->second(image);
  181. }
  182. AZ::u8 ROS2ImageEncodingConversionComponent::GetPriority() const
  183. {
  184. return m_priority;
  185. }
  186. } // namespace ROS2