🎉 75% of content is free forever — Unlock Premium from $10/mo →
CW
NEWSLIVESearch All Content
💼 Servicesℹ️ About✉️ ContactView Pricing Plansfrom $10

Thermal Imaging: Seeing Heat from Above

🟢 Free Lesson

Advertisement

The Electromagnetic Spectrum and Thermal Imaging

Sponsored

Everything above absolute zero (-273.15°C) emits infrared radiation. Thermal cameras detect this invisible heat radiation and convert it into visible images. Unlike regular cameras that see reflected light, thermal cameras see emitted heat — they work in total darkness.

Electromagnetic SpectrumVisible380–700nmInfrared (Thermal)700nm–1mmUVVioletBlueGreenRedNear IRSWIRMWIRLWIRVWIR10nm380nm700nm1μm5μm10μm1mmThermal Imaging BandsMWIR (3–5μm)High sensitivity, cooled sensorsMilitary, gas detectionLWIR (8–14μm)Room temp, uncooled (microbolometer)Drones, building inspectionNear-IR (0.7–1μm)Night vision, NDVIAgriculture dronesStefan-Boltzmann LawP = ε · σ · A · T⁴P = power radiated | ε = emissivity | σ = 5.67×10⁻⁸ W/m²K⁴ | A = area | T = temperature (Kelvin)

How Thermal Cameras Work

A thermal camera contains a microbolometer — an array of tiny temperature-sensitive resistors. Each pixel measures incoming IR radiation and converts it to a temperature reading.

Thermal Camera PipelineIRSceneGermaniumLensMicrobolometerADC14-bitNUCNon-UniformCorrectionColorPaletteMappingCommon Thermal PalettesIronbowRainbowWhite HotBlack HotHeat Map Example: Building Inspection• Blue = cold (20°C)• Yellow/Red = warm (40–60°C) = heat leakDetection: insulation gaps,moisture intrusion, HVAC issues

Thermal Camera Specifications

SpecEntry LevelMid-RangeProfessional
Resolution80×60160×120640×480
NETD<150mK<60mK<30mK
Frame Rate9Hz30Hz60Hz
Lens25° FOV25–50° FOVInterchangeable
ExamplesFLIR Lepton 3.5DJI Zenmuse H20TFLIR A65
python

class ThermalAnalyzer:
    """Basic thermal image analysis."""

    def __init__(self, calibration_offset=0.0):
        self.offset = calibration_offset

    def raw_to_temperature(self, raw_value, emissivity=0.95):
        """Convert raw sensor value to temperature (simplified)."""
        # Simplified conversion (real cameras use complex calibration)
        temp_kelvin = (raw_value / 1000.0) + 273.15 + self.offset
        temp_celsius = temp_kelvin - 273.15
        return temp_celsius

    def analyze_heat_map(self, thermal_image):
        """Extract temperature statistics from thermal image."""
        return {
            "min_temp": np.min(thermal_image),
            "max_temp": np.max(thermal_image),
            "mean_temp": np.mean(thermal_image),
            "std_temp": np.std(thermal_image),
            "hot_spots": np.sum(thermal_image > np.mean(thermal_image) + 2 * np.std(thermal_image)),
            "cold_spots": np.sum(thermal_image < np.mean(thermal_image) - 2 * np.std(thermal_image))
        }

    def detect_anomalies(self, thermal_image, threshold=5.0):
        """Detect temperature anomalies (potential issues)."""
        mean_temp = np.mean(thermal_image)
        anomalies = np.where(np.abs(thermal_image - mean_temp) > threshold)
        return list(zip(anomalies[1], anomalies[0]))  # (x, y) positions

# Demo: Simulated thermal image of a building facade
np.random.seed(42)
thermal_image = np.random.normal(25, 2, (120, 160))  # Base temperature

# Add a heat leak
thermal_image[40:60, 70:90] += 15  # Window heat leak
# Add cold spot (insulation gap)
thermal_image[80:100, 100:120] -= 8

analyzer = ThermalAnalyzer()
stats = analyzer.analyze_heat_map(thermal_image)
anomalies = analyzer.detect_anomalies(thermal_image, threshold=8.0)

print(f"Temperature range: {stats['min_temp']:.1f}°C to {stats['max_temp']:.1f}°C")
print(f"Mean: {stats['mean_temp']:.1f}°C ± {stats['std_temp']:.1f}°C")
print(f"Hot spots detected: {stats['hot_spots']}")
print(f"Anomalies found: {len(anomalies)}")

Hands-On Project: Thermal anomaly detector

Advertisement

Build a system to automatically detect hot spots in thermal images from drone surveys:

python
from collections import deque

class ThermalSurveyAnalyzer:
    """Analyze thermal survey data for anomalies."""

    def __init__(self, flight_altitude=30, gsd=0.1):
        self.flight_altitude = flight_altitude  # meters
        self.gsd = gsd  # ground sampling distance (m/pixel)

    def stitch_thermal_frames(self, frames, positions):
        """Simple thermal frame stitching (naive average)."""
        if not frames:
            return None

        # Simple averaging for overlapping regions
        canvas = np.zeros_like(frames[0], dtype=float)
        count = np.zeros_like(frames[0], dtype=float)

        for frame, pos in zip(frames, positions):
            canvas += frame
            count += 1

        return canvas / np.maximum(count, 1)

    def generate_report(self, thermal_data):
        """Generate a survey report."""
        report = {
            "total_pixels": thermal_data.size,
            "temperature_range": {
                "min": float(np.min(thermal_data)),
                "max": float(np.max(thermal_data)),
                "mean": float(np.mean(thermal_data)),
            },
            "anomalies": [],
        }

        # Define anomaly thresholds
        mean = np.mean(thermal_data)
        std = np.std(thermal_data)

        # Hot anomalies (>2σ above mean)
        hot_mask = thermal_data > mean + 2 * std
        hot_coords = np.where(hot_mask)
        if len(hot_coords[0]) > 0:
            report["anomalies"].append({
                "type": "hot",
                "count": int(np.sum(hot_mask)),
                "max_temp": float(np.max(thermal_data[hot_mask])),
                "locations": list(zip(hot_coords[1].tolist()[:10], hot_coords[0].tolist()[:10]))
            })

        # Cold anomalies (>2σ below mean)
        cold_mask = thermal_data < mean - 2 * std
        cold_coords = np.where(cold_mask)
        if len(cold_coords[0]) > 0:
            report["anomalies"].append({
                "type": "cold",
                "count": int(np.sum(cold_mask)),
                "min_temp": float(np.min(thermal_data[cold_mask])),
                "locations": list(zip(cold_coords[1].tolist()[:10], cold_coords[0].tolist()[:10]))
            })

        return report

# Demo
analyzer = ThermalSurveyAnalyzer(flight_altitude=30, gsd=0.05)

# Simulated thermal survey data
np.random.seed(42)
survey_data = np.random.normal(22, 3, (480, 640))  # Building facade

# Add anomalies
survey_data[100:150, 200:250] += 20  # Heat leak
survey_data[300:350, 400:450] -= 12  # Cold insulation gap

report = analyzer.generate_report(survey_data)

print(f"Survey area: {640 * 0.05:.1f}m × {480 * 0.05:.1f}m")
print(f"Temperature: {report['temperature_range']['min']:.1f}°C to {report['temperature_range']['max']:.1f}°C")
print(f"Anomalies detected: {len(report['anomalies'])}")
for a in report['anomalies']:
    print(f"  - {a['type'].upper()}: {a['count']} pixels")

Key Takeaways

  • Thermal cameras detect infrared radiation (heat), not visible light
  • LWIR (8-14μm) is most common for drone applications (uncooled sensors)
  • Emissivity affects accuracy — different materials radiate heat differently
  • Common palettes: Ironbow (color), White Hot/Black Hot (grayscale)
  • Applications: building inspection, search & rescue, agriculture, industrial monitoring
  • NETD (Noise Equivalent Temperature Difference) indicates sensitivity
—
☆☆☆☆☆
0 ratings

Rate & Feedback

Need Expert Drone AI Help?

Get personalized tutoring, project support, or professional consulting.

Advertisement