Agrarsense
TreeHeightsFromImageComponent.cpp
Go to the documentation of this file.
1// Copyright (c) 2024 FrostBit Software Lab at the Lapland University of Applied Sciences
2//
3// This work is licensed under the terms of the MIT license.
4// For a copy, see <https://opensource.org/licenses/MIT>.
5
7#include <string.h>
8
9UTreeHeightsFromImageComponent::UTreeHeightsFromImageComponent()
10{
11 PrimaryComponentTick.bCanEverTick = false;
12}
13
14void UTreeHeightsFromImageComponent::BeginPlay()
15{
16 Super::BeginPlay();
17}
18
19void UTreeHeightsFromImageComponent::CacheTexture()
20{
21#if WITH_EDITOR
22 FString TexturePath = TEXT("/Game/Agrarsense/Maps/RovaniemiForest/CHM_RovaniemiForest_rescaled.CHM_RovaniemiForest_rescaled");
23 treeMapImage = Cast<UTexture2D>(StaticLoadObject(UTexture2D::StaticClass(), nullptr, *TexturePath));
24 if (treeMapImage)
25 {
26 FTexture2DMipMap& Mip = treeMapImage->GetPlatformData()->Mips[0];
27 void* Data = Mip.BulkData.Lock(LOCK_READ_ONLY);
28 int32 Size = Mip.BulkData.GetBulkDataSize();
29 Pixels.Empty();
30 Pixels.AddUninitialized(Size);
31
32 std::memcpy(Pixels.GetData(), Data, Size);
33
34 Mip.BulkData.Unlock();
35
36 UE_LOG(LogTemp, Warning, TEXT("Tree image cached.."));
37 UE_LOG(LogTemp, Warning, TEXT("Pixels data len %i"), Pixels.Num());
38
39 Width = treeMapImage->GetSizeX();
40 Height = treeMapImage->GetSizeY();
41
42 UE_LOG(LogTemp, Warning, TEXT("Source image resolution: %i x %i"), Width, Height);
43 }
44#endif
45}
46
47float UTreeHeightsFromImageComponent::EuclideanDistance(const FColor& Color1, const FColor& Color2)
48{
49 float R1 = Color1.R;
50 float G1 = Color1.G;
51 float B1 = Color1.B;
52 float R2 = Color2.R;
53 float G2 = Color2.G;
54 float B2 = Color2.B;
55 return FMath::Sqrt(FMath::Square(R2 - R1) + FMath::Square(G2 - G1) + FMath::Square(B2 - B1));
56}
57
58FColor UTreeHeightsFromImageComponent::FindClosestColor(const FColor& TargetColor, const TMap<int32, FColor>& ColorMap)
59{
60 float MinDistance = FLT_MAX;
61 FColor ClosestColor;
62
63 for (const auto& Pair : ColorMap)
64 {
65 float Distance = EuclideanDistance(TargetColor, Pair.Value);
66 if (Distance < MinDistance)
67 {
68 MinDistance = Distance;
69 ClosestColor = Pair.Value;
70 }
71 }
72
73 return ClosestColor;
74}
75
76int32 UTreeHeightsFromImageComponent::GetColorFromPixel(FVector point)
77{
78 //if (!treeMapImage)
79 // return 0;// FColor::Black;
80
81
82 if (Pixels.IsEmpty())
83 {
84 UE_LOG(LogTemp, Warning, TEXT("Returning from Pixels are empty!"));
85 return 0;
86 }
87
88
89
90 //UE_LOG(LogTemp, Warning, TEXT("Coordinate: %f"), point);
91
92 // Vindeln
93 // point.X /= 122.9033;
94 // point.Y /= 122.9033;
95
96
97 // RovaniemiForest
98 point.X /= 190.0;
99 point.Y /= 190.0;
100
101 if (point.X <= 0 || point.Y <= 0)
102 {
103 UE_LOG(LogTemp, Warning, TEXT("Returning from point less than 0"));
104 return 0;// FColor::Black;
105 }
106
107 // Lock the texture to allow access to its data
108 //FTexture2DMipMap& Mip = treeMapImage->GetPlatformData()->Mips[0];
109
110 //if (Mip.BulkData.IsLocked())
111 // return;// FColor::Black;
112
113 //if (Mip.BulkData.IsLocked())
114 // Mip.BulkData.Unlock();
115
116 //void* Data = Mip.BulkData.Lock(LOCK_READ_ONLY);
117
118 //if (!Data)
119 //{
120 // Mip.BulkData.Unlock();
121 // return FColor::Black;
122 //}
123
124
125 // Get image resolution
126 /*Width = treeMapImage->GetSizeX();
127 Height = treeMapImage->GetSizeY();*/
128
129 if (point.X > Width || point.Y > Height)
130 {
131 //Mip.BulkData.Unlock();
132 UE_LOG(LogTemp, Warning, TEXT("Returning from point over the resolution"));
133 return 0;
134 }
135
136 // If width and height not good, return black
137 if (Width <= 0 || Height <= 0)
138 {
139 //if (Mip.BulkData.IsLocked())
140 //Mip.BulkData.Unlock();
141 UE_LOG(LogTemp, Warning, TEXT("Returning from width <= 0"));
142 return 0;// FColor::Black;
143 }
144
145
146 //uint8* Pixels = static_cast<uint8*>(Data);
147
148 // New version, confirmed working on RovaniemiForest
149 const int64 Base = ((int64)point.Y * (int64)Width + (int64)point.X) * 4;
150
151 if (Base >= 0 && Base + 3 < Pixels.Num())
152 {
153 // Swap RGBA -> BGRA
154 const uint8 B = Pixels[(int32)Base + 0];
155 const uint8 G = Pixels[(int32)Base + 1];
156 const uint8 R = Pixels[(int32)Base + 2];
157 // const uint8 A = Pixels[(int32)Base + 3];
158
159 const FColor PixelColor(R, G, B);
160
161 const FColor Closest = FindClosestColor(PixelColor, colorHeightRovaniemi);
162 for (const auto& Pair : colorHeightRovaniemi)
163 {
164 if (Pair.Value == Closest)
165 {
166 UE_LOG(LogTemp, Warning, TEXT("This tree is : %i m"), Pair.Key);
167 return Pair.Key;
168 }
169 }
170 }
171 else
172 {
173 UE_LOG(LogTemp, Warning, TEXT("Index out of range"));
174 }
175
176 // Old version, works well in Vindeln
177
178 // int32 Index = ((int32)point.Y * Width + (int32)point.X) * 4; // 4 bytes per pixel (RGBA)
179 //
180 // if (Index + 3 <= Pixels.Num())
181 // {
182 // uint8 R = Pixels[Index];
183 // uint8 G = Pixels[Index + 1];
184 // uint8 B = Pixels[Index + 2];
185 // //uint8 A = Pixels[Index + 3];
186 //
187 // FColor pixelColor = FColor(B, G, R);
188 //
189 // FColor closestColor = FindClosestColor(pixelColor, colorHeightRovaniemi);
190 //
191 // for (const auto& Pair : colorHeightRovaniemi)
192 // {
193 // if (Pair.Value == closestColor)
194 // {
195 // UE_LOG(LogTemp, Warning, TEXT("This tree is : %i m"), Pair.Key);
196 // return Pair.Key;
197 // }
198 // }
199 // }
200 // else {
201 // UE_LOG(LogTemp, Warning, TEXT("Index out of range"));
202 // }
203
204
205
206 // Unlock the texture
207 //Mip.BulkData.Unlock();
208
209 //return pixelColor;
210 return 0;
211}
212
213#if WITH_EDITOR
214void UTreeHeightsFromImageComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
215{
216 Super::PostEditChangeProperty(PropertyChangedEvent);
217
218 // Check if the custom button trigger has been toggled
219 if (PropertyChangedEvent.Property != nullptr && PropertyChangedEvent.Property->GetName() == "GenerateTrigger")
220 {
221 if (GenerateTrigger)
222 {
223 CacheTexture();
224 GenerateTrigger = false;
225 }
226 }
227}
228#endif