Contents

Periodic sharpness and luminance shifts in DJI footage

How to establish whether a repeating frame-to-frame artifact really is GOP pulsing, and what to do about it once you know where it comes from.

August 2026  ·  DaVinci Resolve 18 and later  ·  DJI H.264 / H.265  ·  Commands verified against FFmpeg 6.1

How to use this document

Sections 1 to 3 explain the mechanism. If you want to get straight to work, start at Section 4, which routes you to the right fix in four tests, then follow the section it sends you to. Appendix A collects every command in one place. Appendix B is a script that tests, objectively, whether your pulse is actually aligned with picture type.

1.

The symptom, and what it does not prove

One frame in a repeating sequence looks slightly crisper, noisier, or different in texture from its neighbours. Often the waveform or RGB parade moves by a small amount at the same cadence. It shows up worst in fine, low-contrast texture: foliage, grass, gravel, roof tiles, choppy water, distant urban detail. On a small monitor it can read as a faint shimmer rather than a sharpness change. On scopes it is obvious.

A stable interval is useful evidence, but on its own it does not establish a compression cause. Decoder behaviour, temporal processing, sharpening, denoising, frame interpolation and the delivery encode can all produce a periodic artifact. Treat the coding explanation as the leading hypothesis and then test it.

What a regular cadence does make unlikely

CauseWhy it is a poor fit
Propeller in shotProduces a moving dark band or localised blur that drifts against the frame rate rather than locking to an exact frame count, and it does not change texture across the whole frame.
Mains or LED flickerGives a smooth luminance ripple or rolling bands, not a one-frame change in texture.
Rolling shutterProduces geometric skew during fast movement, not a periodic sharpness change.
Auto exposure or variable NDSteps follow scene brightness, so they are aperiodic, and they move the whole trace rather than only the fine detail.
Gimbal micro-jitterWould blur individual frames. Here the odd frame out is the sharper one.

These are unlikely, not impossible. A repeating camera move over repeating scene content can coincidentally align with a frame interval. That is why Section 4 exists.

Clues that strengthen the coding hypothesis

  • The effect repeats at a stable frame interval.
  • It is strongest in fine, low-contrast texture.
  • It survives an independent decode outside Resolve.
  • The visual cadence lines up with measured picture types or keyframe positions in that specific file. This is the strongest single piece of evidence, and Section 5 shows how to get it.
2.

How long-GOP compression can produce it

DJI drones do not store every frame as a complete image. H.264 and H.265 encode a mixture of picture types within a Group of Pictures:

  • I-picture (intra, keyframe). Coded independently, without reference to any other picture.
  • P-picture (predicted). Uses prediction from one or more previously coded pictures, storing motion vectors plus a residual.
  • B-picture (bidirectional). Uses prediction from pictures on both sides. Usually the cheapest.

A "GOP of 10" means an I-picture every ten frames. At 25 fps that is one every 0.4 seconds. Do not assume this value. It varies with camera, firmware, frame rate, codec and recording mode, and Section 5 measures it in seconds.

Data allocated across one group of pictures Schematic bar chart of fifteen consecutive frames labelled I, B, B, P, B, B, P, B, B, P, I, B, B, P, B. Bar height stands for the data allocated to each picture. The two intra pictures are much the taller, the P pictures are middling and the B pictures are the shortest. A bracket marks the first ten frames as one group of pictures. data allocated I B B P B B P B B P I B B P B ONE GROUP OF PICTURES
Schematic only. Bar height stands for data allocated to each picture. The pattern of a large intra-coded picture followed by much smaller predicted pictures is common, but the exact structure is encoder-dependent.

Why the intra picture can look different

The camera works to a bitrate ceiling set by the recording mode and has to divide that budget across the GOP. Encoders generally quantise intra pictures less aggressively than predicted ones, because a cleaner reference keeps later pictures cleaner. Software encoders expose this directly: x264 and x265 call it ip-ratio, default 1.4. Hardware encoders in cameras apply the same principle internally.

What can legitimately be measured

  • Picture type (I, P, B), where the bitstream and decoder expose it.
  • Packet size per frame, which can vary substantially.
  • Timestamps and frame order.
  • Image-domain measures such as high-frequency energy or edge strength.
  • Whether a visible pulse is temporally aligned with a repeating coding structure.

An illustration of the size difference

The table below comes from a controlled synthetic test encode, not from a DJI camera: 1280x720, deliberately bitrate-starved, GOP of 10, two B-pictures. It shows that picture types can receive very different amounts of data. It says nothing about what any particular drone does.

FrameTypeBytesHF energy
10I13,324218.6
11B2,040173.7
13P5,355173.3
15B2,200155.8
17B2,444158.0
19P6,042163.8
20I14,597258.6

Abridged. Across the full range the intra pictures averaged 1.45 times the high-frequency energy of the others. The same script run on a well-fed encode of the same source returned 1.05 times, which is the negative control.

3.

Why grading reveals it

Log recordings hold a wide dynamic range in a narrow, low-contrast signal range. Normalising expands that signal, and small differences between pictures expand with it. Shadow lifting, contrast, sharpening and saturation all make the result more conspicuous.

A more useful mental model is a chain, where each stage adds visibility:

subtle difference in the source encode    normalisation and contrast    more visible    sharpening and detail enhancement    more visible still

Bit depth

10-bit provides 1024 code values per channel against 256 for 8-bit. That is substantially more room before quantisation becomes objectionable under a heavy grade. It does not make a coding artifact disappear, and 8-bit log is where this problem is hardest to avoid.

Chroma

Recording formats vary by aircraft and mode. Check DJI's published specification for the specific model rather than generalising from another drone.

4.

Locate the stage before you fix anything

Diagnostic router

Answer each test to be routed to the matching fix. Nothing is hidden: every test stays on the page.

Four stages can produce this symptom and the remedies do not transfer between them. Work through these in order and stop when you find where it enters.

Test 1Is it in the original file?

Decode a short range with FFmpeg, which bypasses every setting in Resolve, and compare consecutive frames at 100 percent on an area of fine texture.

shell
ffmpeg -i DJI_0001.MP4 -vf "select=between(n\,100\,130)" \
  -fps_mode passthrough frame_%06d.png

Output is numbered sequentially from 1, so frame_000011.png is source frame 110.

Which is it?

Test 2Is Resolve decoding differently?Studio

In Resolve Studio, go to Preferences System Decode Options, disable hardware-accelerated H.264 and H.265 decoding, quit and relaunch so the change takes effect, then compare against the FFmpeg frames again.

Which is it?

Test 3Is the grade amplifying it?

Disable all nodes and inspect the same frames on the scopes. Re-enable nodes one at a time.

Which is it?

Test 4Is the export introducing it?

Render a short section to an intra-frame intermediate such as ProRes or DNxHR and inspect that.

Which is it?

5.

Measure the coding structure

This takes under a minute and converts a hunch into evidence. Run it on the original file, not a transcode.

List picture types, numbered by frame

shell
ffprobe -v error -select_streams v:0 \
  -show_entries frame=pict_type -of csv=p=0 DJI_0001.MP4 \
  | head -60 | cat -n

Output is in presentation order, so the numbers correspond to timeline frames. A GOP of 10 looks like this:

output
     1  I          11  I
     2  B          12  B
     3  P   ...    13  B
    10  P          21  I

List keyframe timestamps only (faster on long clips)

shell
ffprobe -v error -select_streams v:0 -skip_frame nokey \
  -show_entries frame=pts_time -of csv=p=0 DJI_0001.MP4 | head -20

Show data allocated per picture, printed as size,type

shell
ffprobe -v error -select_streams v:0 \
  -show_entries frame=pkt_size,pict_type -of csv=p=0 DJI_0001.MP4 \
  | head -30 | cat -n
6.

Fixes, by where the artifact enters

6A. The variation is in the source encode

You cannot reconstruct information the camera never wrote. The goal is to reduce the remaining temporal difference.

1. Temporal noise reductionStudio

Colour page, Motion Effects panel, or ResolveFX Revival Noise Reduction. Studio only. This is the sensible first experiment, because it combines information across neighbouring frames and an outlying picture is pulled towards its neighbours.

  • Frames: 2. Try 3 only on near-static shots.
  • Motion Estimation: Better rather than Faster, which tends to smear on drone moves.
  • Motion Range: match the shot. Medium suits a slow push or orbit.
  • Temporal Threshold: start low, around 3 to 6 on luma, and raise until the pulse settles.

These are starting points for testing, not optimum values, and control names vary between Resolve versions. Watch for ghosting on fast pans and smearing in moving foliage; if you see either, lower the threshold rather than switching to Faster.

2. DeflickerStudio

ResolveFX Revival Deflicker. Studio only. Designed for periodic luminance variation, so it can settle the waveform step. It tends to do less for a texture or sharpness pulse, so test it rather than assuming it addresses the right thing. It includes its own Temporal NR section and a Restore Original Detail After Deflicker section. Use low strength, and use it alongside temporal NR rather than instead of it.

3. Downscale

Underrated. If you are delivering 1080 from a higher-resolution source, the downscale averages neighbouring pixels and can make fine periodic differences much less visible at no cost. Test it before reaching for anything stronger.

4. GrainStudio in part

A light grain gives the eye constant high-frequency texture, which masks a periodic change well. Cosmetic rather than corrective, and it increases the demand on your delivery encoder, so raise the export bitrate to match. Resolve's Film Grain effect is Studio only; grain plates work on the free edition.

5. Slight softening

A last resort. On the Colour page Blur palette the radius default is 0.50, where higher values blur and lower values sharpen. Moving to 0.51 or 0.52 reduces the peak difference without visibly softening a wide shot. Treat this as an aesthetic compromise, not a repair.

6B. Resolve is decoding differently

  1. StudioLeave hardware decode disabled (Preferences System Decode Options, Studio only) and confirm the improvement holds across several clips.
  2. Update GPU drivers. On NVIDIA, prefer the Studio driver branch.
  3. Transcode outside Resolve to an intra-frame codec and cut with that. This removes Resolve's H.264 and H.265 decoder from the equation entirely, and as a bonus every frame becomes independently decodable, so scrubbing gets much faster.
shell
# ProRes 422 HQ
ffmpeg -i DJI_0001.MP4 -c:v prores_ks -profile:v 3 \
  -pix_fmt yuv422p10le -c:a pcm_s16le DJI_0001_prores.mov
shell
# DNxHR HQX
ffmpeg -i DJI_0001.MP4 -c:v dnxhd -profile:v dnxhr_hqx \
  -pix_fmt yuv422p10le -c:a pcm_s16le DJI_0001_dnxhr.mov

For ProRes, -profile:v 3 is 422 HQ and -profile:v 2 is standard 422 if storage is tight.

6C. The grade is amplifying it

  • Remove unnecessary sharpening. DJI already sharpens in camera, and additional sharpening directly increases the difference you are trying to hide.
  • Normalise with a colour space transform rather than adding contrast through a creative LUT that was not designed for the job.
  • Soften steep contrast curves. A surprising proportion of the visible effect often comes from the last part of the contrast you added.
  • Grade in a wide gamut intermediate so you are not compounding expansions in a display-referred space.

6D. The delivery encode is introducing it

  • Raise quality or bitrate and compare. Values in the region of 50 to 120 Mbps are reasonable test points for demanding 4K material. They are not thresholds: the requirement depends on resolution, frame rate, codec, content complexity and encoder implementation.
  • Use quality-based rate control where the encoder offers it, rather than a low constant bitrate.
  • Test a shorter keyframe interval if the encoder exposes it. This reduces the distance between intra-coded pictures, but it also changes compression efficiency and bitrate requirements, so measure rather than assume.
  • Account for grain. Grain is expensive to encode and competes with real texture for the bit budget. Grain plus a low bitrate reliably produces this artifact.
  • Keep an intra-frame master in ProRes or DNxHR and generate delivery encodes from it. This does not repair a source artifact, but it prevents an unnecessary extra long-GOP generation from becoming a new one.
7.

Prevention on the next shoot

The aim is to give the camera's encoder an easier problem while keeping the dynamic range you actually need.

  1. Do not shoot log by default. If the scene does not need the extra range, a standard profile applies contrast before compression, so there is far less expansion required in post and therefore less to amplify. This is the most effective single measure and the easiest to overlook.
  2. Use 10-bit where available if you expect to grade log heavily.
  3. Use the highest practical quality and bitrate setting. More room for difficult texture.
  4. Expose log sensibly. Strongly underexposed shadows lifted later is where quantisation and compression limits show first.
  5. Keep noise under control. Fine noise consumes coding capacity that real detail needed.
  6. Respect difficult textures. Dense foliage, grass, gravel and choppy water are expensive to encode. Slow complex moves across them down.
  7. Test H.264 against H.265 on the actual aircraft. Efficiency and encoder implementation are model-specific, so shoot a comparison over grass rather than following a general rule.
  8. Choose between D-Log and D-Log M on the shot, not on a rule. Where a model offers both, D-Log M may need less aggressive normalisation, but the decision should follow the dynamic range the scene actually requires. Supported profiles and bit depths differ by model and mode.
8.

Quick reference

Where it entersHow you establish itWhat to try
Source encodeVisible in independently decoded FFmpeg frames, and cadence matches measured picture typesTemporal NR at 2 frames; test downscale; reduce sharpening and contrast; grain or slight softening as cosmetic last resorts (6A)
Resolve decoderFFmpeg frames clean, Resolve differsDisable hardware decode and relaunch (Studio); update GPU drivers; transcode to ProRes 422 HQ or DNxHR HQX (6B)
The gradeAppears or worsens markedly only with specific nodes enabledRemove sharpening; normalise with a colour space transform; soften contrast curves (6C)
Delivery encodeIntra-frame intermediate clean, delivered file pulsesRaise quality or bitrate; quality-based rate control; test keyframe interval; master to ProRes (6D)

Which tools require Resolve Studio

ToolEdition
Preferences System Decode OptionsStudio
Temporal and spatial noise reductionStudio
ResolveFX Revival, including DeflickerStudio
Film GrainStudio
Colour space transform, Blur palette, scopesFree and Studio
Everything in Appendices A and BNeither, these are FFmpeg
9.

When you may call it GOP pulsing

"Every tenth frame looks sharper" is not a diagnosis. The claim is well supported when most of the following hold:

  1. The effect is visible in an independently decoded copy of the original file.
  2. It repeats at a stable cadence.
  3. That cadence corresponds to a measurable coding structure in that file, not in a different clip or a different camera.
  4. Frame size or high-frequency energy shows a related repeating pattern aligned to picture type.
  5. The effect survives removal of the grade, or the grade's amplifying role can be demonstrated separately.
  6. Where export is suspected, an intra-frame intermediate stays clean while a subsequent long-GOP encode recreates the pattern.

If those do not agree, do not call it GOP pulsing yet. Call it a periodic frame-to-frame artifact and keep isolating the stage responsible. The practical cost of the wrong label is real: it sends you to temporal NR when the actual fix was a render setting.

A.

Command reference

Verified against FFmpeg 6.1. Replace DJI_0001.MP4 with the clip name.

Picture type per frame, numbered

shell
ffprobe -v error -select_streams v:0 \
  -show_entries frame=pict_type -of csv=p=0 DJI_0001.MP4 \
  | head -60 | cat -n

Keyframe timestamps only

shell
ffprobe -v error -select_streams v:0 -skip_frame nokey \
  -show_entries frame=pts_time -of csv=p=0 DJI_0001.MP4 | head -20

Data per picture, as size,type

shell
ffprobe -v error -select_streams v:0 \
  -show_entries frame=pkt_size,pict_type -of csv=p=0 DJI_0001.MP4 \
  | head -30 | cat -n

Stream summary: codec, profile, bit depth, frame rate

shell
ffprobe -v error -select_streams v:0 -show_entries \
  stream=codec_name,profile,pix_fmt,bit_rate,r_frame_rate \
  -of default=noprint_wrappers=1 DJI_0001.MP4

Decode a frame range to PNG

shell
ffmpeg -i DJI_0001.MP4 -vf "select=between(n\,100\,130)" \
  -fps_mode passthrough frame_%06d.png

Transcode to ProRes 422 HQ

shell
ffmpeg -i DJI_0001.MP4 -c:v prores_ks -profile:v 3 \
  -pix_fmt yuv422p10le -c:a pcm_s16le DJI_0001_prores.mov

Transcode to DNxHR HQX

shell
ffmpeg -i DJI_0001.MP4 -c:v dnxhd -profile:v dnxhr_hqx \
  -pix_fmt yuv422p10le -c:a pcm_s16le DJI_0001_dnxhr.mov

Batch a folder to ProRes

shell
for f in *.MP4; do
  ffmpeg -i "$f" -c:v prores_ks -profile:v 3 \
    -pix_fmt yuv422p10le -c:a pcm_s16le "${f%.MP4}_prores.mov"
done
B.

Testing alignment objectively

This script answers the question that matters: is the pulse actually aligned with picture type, or does it only look periodic? It prints per-frame high-frequency energy beside picture type, then reports how far the intra pictures separate from the rest.

Requires FFmpeg on the path plus pip install numpy pillow. Run it as python3 gop_pulse.py DJI_0001.MP4 100 40 to test forty frames starting at frame 100.

Interpreting the separation figure

  • Above about 1.10 means the intra pictures carry measurably more high-frequency content, which is consistent with a coding-related cause. Corroborate it against Section 9 before concluding.
  • Around 1.00 means picture type does not predict the effect in that range. Look elsewhere, and widen the range in case it contained too few intra pictures to be meaningful.
  • Remember what is being measured. Noise, ringing and sharpening all raise this figure. It tests alignment, not quality.
python
#!/usr/bin/env python3
"""Test whether a visible pulse is aligned with picture type.

Usage:  python3 gop_pulse.py CLIP.MP4 [start_frame] [num_frames]
Needs:  ffmpeg and ffprobe on PATH, plus  pip install numpy pillow

Reports Laplacian variance per frame. A HIGH VALUE MEANS MORE HIGH-FREQUENCY
CONTENT, NOT MORE GENUINE DETAIL: noise, ringing and sharpening raise it too.
Use this to test alignment with picture type, never as a quality measure.
"""
import subprocess, sys, tempfile, pathlib
import numpy as np
from PIL import Image

clip  = sys.argv[1]
start = int(sys.argv[2]) if len(sys.argv) > 2 else 0
count = int(sys.argv[3]) if len(sys.argv) > 3 else 60
end   = start + count - 1

# Picture type of every frame, in presentation order.
probe = subprocess.run(
    ["ffprobe", "-v", "error", "-select_streams", "v:0",
     "-show_entries", "frame=pict_type", "-of", "csv=p=0", clip],
    capture_output=True, text=True, check=True)
types = [t.strip(",") for t in probe.stdout.split()]

# Decode the range with sequential numbering, so nothing depends on how the
# container stores timestamps.
tmp = pathlib.Path(tempfile.mkdtemp())
subprocess.run(
    ["ffmpeg", "-v", "error", "-i", clip,
     "-vf", f"select=between(n\\,{start}\\,{end})",
     "-fps_mode", "passthrough",
     str(tmp / "f_%06d.png")], check=True)

pngs = sorted(tmp.glob("f_*.png"))
if len(pngs) != count:
    sys.exit(f"Extracted {len(pngs)} frames but asked for {count}. The range "
             f"probably runs past the end of the clip, so frame numbering "
             f"would be wrong. Stopping rather than reporting bad numbers.")

K = np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]], dtype=np.float64)

def hf_energy(png):
    g = np.asarray(Image.open(png).convert("L"), dtype=np.float64)
    h, w = g.shape
    lap = sum(K[i, j] * g[i:h - 2 + i, j:w - 2 + j]
              for i in range(3) for j in range(3) if K[i, j])
    return lap.var()

rows = [(start + off, types[start + off] if start + off < len(types) else "?",
         hf_energy(p)) for off, p in enumerate(pngs)]

base = np.median([v for _, _, v in rows])
print(f"{'frame':>7}  {'type':<4}  {'hf energy':>10}  {'vs median':>9}")
for n, t, v in rows:
    bar = "#" * int(round(30 * v / (base * 1.5))) if base else ""
    print(f"{n:>7}  {t:<4}  {v:>10.1f}  {v / base:>8.2f}x  {bar}")

# The question that matters: does picture type predict the pulse?
i_vals = [v for _, t, v in rows if t == "I"]
o_vals = [v for _, t, v in rows if t != "I"]
print()
if not i_vals:
    print("No I-picture in this range. Widen it and run again.")
elif not o_vals:
    print("All-intra stream. Picture type cannot explain a pulse here.")
else:
    ratio = float(np.mean(i_vals) / np.mean(o_vals))
    print(f"I-pictures  n={len(i_vals):<3} mean {np.mean(i_vals):8.1f}")
    print(f"Others      n={len(o_vals):<3} mean {np.mean(o_vals):8.1f}")
    print(f"Separation  {ratio:.3f}x")
    if ratio > 1.10:
        print("I-pictures carry measurably more high-frequency content.")
        print("Consistent with a coding-related cause. Corroborate before concluding.")
    else:
        print("No clear separation by picture type across this range.")
        print("Look for another cause before calling this GOP pulsing.")

Example output

output
  frame  type   hf energy  vs median
     10  I          218.6      1.26x  #########################
     11  B          173.7      1.00x  ####################
     13  P          173.3      1.00x  ####################
     15  B          155.8      0.90x  ##################
     19  P          163.8      0.95x  ###################
     20  I          258.6      1.49x  ##############################

I-pictures  n=3   mean    249.1
Others      n=18  mean    172.3
Separation  1.446x
I-pictures carry measurably more high-frequency content.
Consistent with a coding-related cause. Corroborate before concluding.

Every FFmpeg and ffprobe command in this document was executed and its output checked. The chroma quantisation behaviour described in Section 3 follows the chroma QP derivation defined in H.264 and HEVC. Resolve edition requirements follow Blackmagic's published feature documentation. Figures drawn from controlled test encodes are labelled as such and should not be read as characteristic of any DJI product; measure your own file.