Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Exposure for Plug-in
#1
There is no pdb call for Exposure setting.
How do I do the equivalent?
Thank you in advance
Reply
#2
I even went as far as trying to read code gegl/exposure.c
and can't get it to produce the same
here's my python call
def simulate_gimp_exposure(img, drawable, black_level=0.002, exposure=1.696):
pdb.gimp_image_undo_group_start(img)

# Get the current layer
layer = drawable

# Get the pixel data
width, height = layer.width, layer.height

srcRgn = layer.get_pixel_rgn(0, 0, width, height,
False, False)
src_pixels = array("B", srcRgn[0:width, 0:height])

dstRgn = layer.get_pixel_rgn(0, 0, width, height,
True, True)
p_size = len(srcRgn[0,0])
dest_pixels = array("B", "\x00" * (width * height * p_size))

# white = math.pow(2.0, -exposure) # Equivalent to exp2f(-exposure)
# diff = max(white - black_level, 0.000001)
# gain = 1.0 / diff # Normalize brightness scaling
# **Apply Exposure Negation** (Key part of GIMP logic)
exposure_negated = -exposure # ✅ This is how GIMP negates exposure
white = math.pow(2.0, exposure_negated) # ✅ Equivalent to exp2f(exposure_negated)

# Ensure we don't divide by zero in the gain calculation
diff = max(white - black_level, 0.000001)
gain = 1.0 / diff # Normalize brightness scaling

# Read pixel data as bytes
#data = bytearray(srcRgn[0:width, 0:height]) # Convert to mutable array
data = src_pixels

# Determine number of channels (RGB = 3, RGBA = 4)
bpp = len(data) // (width * height)
p_size = bpp
dest_pixels = array("B", "\x00" * (width * height * p_size))
# Convert to float, apply black level & exposure, and store back
exposure_factor = 2**exposure
for i in range(0, len(data), bpp):
r, g, b = [float(data[i + j])/255.0 for j in range(3)]
a = data[i+3]

# **Apply GIMP Exposure Math**
r = (r - black_level) * gain
g = (g - black_level) * gain
b = (b - black_level) * gain

# Clip values to (0,1) range
r, g, b = [min(max(x, 0.0), 1.0) for x in (r, g, b)]

# Convert back to 8-bit (0-255)
r, g, b = [int(x * 255) for x in (r, g, b)]

dest_pixels[i] = r
dest_pixels[i+1] = g
dest_pixels[i+2] = b
dest_pixels[i+3] = a

# Copy the whole array back to the pixel region:


# Apply modified data back to the image

#pixel_rgn[0:width, 0:height] = bytes(data)
#pixel_rgn.set_pixels(0, 0, width, height, bytes(data))
dstRgn[0:width, 0:height] = dest_pixels.tostring()
#pixel_rgn[0:width, 0:height] = dest_pixels.tostring()
layer.flush()
layer.merge_shadow(True)
layer.update(0, 0, width, height) # Mark the region as modified
pdb.gimp_image_undo_group_end(img)
pdb.gimp_displays_flush()
Reply
#3
(02-13-2025, 02:42 PM)trandoductin Wrote: There is no pdb call for Exposure setting.
How do I do the equivalent?
Thank you in advance

It's a GEGL filter. Somewhat easy in GIMP3. In Gimp2, as far as I cant tell the steps are photographic exposure values (aka EV), and one EV is twice as more light. So you convert the value to linear, and multiply by 2^exposure_correction. Of course you don't do this on a pixel basis (unless you have numpy) but you can compute several values for Curves and then apply Curves. I didn't try but you can tell Gimp 2.10 that your plugin understands linear mode (pdb.gimp_plugin_enable_precision()) and this may avoids having to do sRGB<->Linear conversions.Otherwise you can help yourself to my Python code.
Reply
#4
(02-13-2025, 09:29 PM)trandoductin Wrote: I even went as far as trying to read code gegl/exposure.c
and can't get it to produce the same

The code you see is applied on linear values. See my other post.
Reply
#5
But the I wrote my python code based on GEGL code as seen below
static void
process_rgba (GeglOperation *op,
void *in_buf,
void *out_buf,
glong n_pixels,
const GeglRectangle *roi,
gint level)
{
GeglProperties *o = GEGL_PROPERTIES (op);
gfloat *in_pixel;
gfloat *out_pixel;
gfloat black_level = (gfloat) o->black_level;
gfloat diff;
gfloat exposure_negated = (gfloat) -o->exposure;
gfloat gain;
gfloat white;

glong i;

in_pixel = in_buf;
out_pixel = out_buf;

white = exp2f (exposure_negated);
diff = MAX (white - black_level, 0.000001);
gain = 1.0f / diff;

for (i=0; i<n_pixels; i++)
{
out_pixel[0] = (in_pixel[0] - black_level) * gain;
out_pixel[1] = (in_pixel[1] - black_level) * gain;
out_pixel[2] = (in_pixel[2] - black_level) * gain;
out_pixel[3] = in_pixel[3];

out_pixel += 4;
in_pixel += 4;
}
}

Hackathon,
Ran my function on a layer and delete half of it.
Then ran exposure on the original other half and see how much darker it is.
So I used
simulate_gimp_exposure(image,layer,0.002,1.696/2.05) #dividing 2.1-ish or 2.05 to match for specific 1.696 exposure.
pretty close I hope it's good enough for my client
Reply
#6
(02-14-2025, 12:17 AM)trandoductin Wrote: But the I wrote my python code based on GEGL code as seen below

GEGL works on linear values. Nowhere in the code you show does it say that the 0.0 .. 1.0 values are gamma-corrected. Using the spreadsheet functions from  the tutorial I linked, I get Gimp's results if I convert to linear first.


Attached Files
.zip   Exposure.zip (Size: 23.55 KB / Downloads: 9)
Reply
#7
Ok this is above my head
Reply
#8
Why not use the GEGL filter. One of your other posts here. https://www.gimp-forum.net/Thread-GEGL-k...9#pid41869
Reply
#9
(02-16-2025, 03:10 AM)trandoductin Wrote: Ok this is above my head

Did you read the tutorial I linked to?
Reply
#10
Yeah skimmed it over and something about gamma but their is no gamma in GEGL code that I can see
Reply


Forum Jump: