Skip to content

Commit ac4bf1c

Browse files
committed
Add FLIR sample code
1 parent 16a0cd3 commit ac4bf1c

File tree

2 files changed

+123
-6
lines changed

2 files changed

+123
-6
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Copyright (c) Drew Noakes and contributors. All Rights Reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
2+
3+
using System.Diagnostics.CodeAnalysis;
4+
using System.Drawing;
5+
using System.Drawing.Imaging;
6+
using System.IO;
7+
using System.Linq;
8+
using System.Runtime.InteropServices;
9+
using System.Windows.Media;
10+
using System.Windows.Media.Imaging;
11+
using MetadataExtractor.Formats.Flir;
12+
using MetadataExtractor.Formats.Jpeg;
13+
using MetadataExtractor.Util;
14+
15+
namespace MetadataExtractor.Samples
16+
{
17+
public static class FlirSamples
18+
{
19+
public static void Main()
20+
{
21+
var inputFile = "my-input.jpg"; // path to a FLIR JPEG file
22+
var outputFile = "my-output.png"; // path to the output thermal image
23+
24+
if (TryGetThermalImageBytesFromJpeg(inputFile, out byte[]? bytes, out int width, out int height))
25+
{
26+
WritePng(bytes, width, height, outputFile);
27+
}
28+
}
29+
30+
public static bool TryGetThermalImageBytesFromJpeg(
31+
string jpegFile,
32+
[NotNullWhen(returnValue: true)] out byte[]? imageBytes,
33+
out int width,
34+
out int height)
35+
{
36+
var readers = JpegMetadataReader
37+
.AllReaders
38+
.Where(reader => reader is not FlirReader)
39+
.Concat(new[] { new FlirReader { ExtractRawThermalImage = true } })
40+
.ToList();
41+
42+
var directories = JpegMetadataReader.ReadMetadata(jpegFile, readers);
43+
44+
var flirRawDataDirectory = directories.OfType<FlirRawDataDirectory>().FirstOrDefault();
45+
46+
if (flirRawDataDirectory is null)
47+
{
48+
imageBytes = null;
49+
width = 0;
50+
height = 0;
51+
return false;
52+
}
53+
54+
width = flirRawDataDirectory.GetInt32(FlirRawDataDirectory.TagRawThermalImageWidth);
55+
height = flirRawDataDirectory.GetInt32(FlirRawDataDirectory.TagRawThermalImageHeight);
56+
imageBytes = flirRawDataDirectory.GetByteArray(FlirRawDataDirectory.TagRawThermalImage);
57+
58+
return imageBytes != null;
59+
}
60+
61+
public static void WritePng(byte[] thermalImageBytes, int width, int height, string outputFile)
62+
{
63+
var fileType = FileTypeDetector.DetectFileType(new MemoryStream(thermalImageBytes));
64+
65+
if (fileType == FileType.Png)
66+
{
67+
// Data is already in PNG format.
68+
// It is likely already coloured and ready for presentation to a human.
69+
70+
File.WriteAllBytes(outputFile, thermalImageBytes);
71+
return;
72+
}
73+
74+
// Assume data is in uint16 grayscale.
75+
//
76+
// It is "raw" meaning uncoloured but, more importantly, the levels are unadjusted.
77+
// For example, if the scene did not have much temperature variation relative to the
78+
// range of the sensor, most values may be within a very narrow band of the 16-bit spectrum,
79+
// making the image appear a flat gray. Opening it in Photoshop/Gimp/etc and adjusting levels
80+
// will reveal the image.
81+
//
82+
// There is other metadata in the image that may inform this process (untested -- please
83+
// share if you find a good process here).
84+
85+
var pixelFormats = PixelFormats.Gray16;
86+
87+
var bitmap = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);
88+
89+
var data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
90+
91+
Marshal.Copy(thermalImageBytes, 0, data.Scan0, thermalImageBytes.Length);
92+
93+
var source = BitmapSource.Create(
94+
width,
95+
height,
96+
bitmap.HorizontalResolution,
97+
bitmap.VerticalResolution,
98+
pixelFormats,
99+
null,
100+
data.Scan0,
101+
data.Stride * height,
102+
data.Stride);
103+
104+
bitmap.UnlockBits(data);
105+
106+
using var stream = new FileStream(outputFile, FileMode.Create);
107+
108+
var encoder = new PngBitmapEncoder();
109+
encoder.Frames.Add(BitmapFrame.Create(source));
110+
encoder.Save(stream);
111+
}
112+
}
113+
}
Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>net5.0;net35;net45</TargetFrameworks>
4+
<TargetFramework>net48</TargetFramework>
55
<OutputType>Exe</OutputType>
6+
<StartupObject>MetadataExtractor.Samples.Program</StartupObject>
67
</PropertyGroup>
78

89
<ItemGroup>
910
<ProjectReference Include="..\MetadataExtractor\MetadataExtractor.csproj" />
1011
</ItemGroup>
1112

12-
<ItemGroup Condition=" '$(TargetFramework)' == 'net35' ">
13-
<Reference Include="System" />
13+
<ItemGroup>
14+
<PackageReference Include="Nullable" Version="1.3.0">
15+
<PrivateAssets>all</PrivateAssets>
16+
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
17+
</PackageReference>
1418
</ItemGroup>
1519

16-
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
17-
<Reference Include="System" />
18-
<Reference Include="Microsoft.CSharp" />
20+
<ItemGroup>
21+
<Reference Include="PresentationCore" />
22+
<Reference Include="WindowsBase" />
1923
</ItemGroup>
2024

2125
</Project>

0 commit comments

Comments
 (0)