libvips / lua-vips

Lua binding for the libvips image processing library
MIT License
129 stars 10 forks source link

Threshold to mask #55

Closed rrrnld closed 1 year ago

rrrnld commented 1 year ago

Heya! Thanks for working on vips and for being so helpful to people like me that come around with questions on how to use it. I appreciate that a lot!

I'd like to accomplish the following thing:

  1. Load an image (jpeg most likely)
  2. Create an image masking out the brightest 1% and the lowest 30% of the image.
  3. Get the resulting non-masked images in a color space of my choice

nips2 has this really handy Image_select_item.Threshold_percent_item.action, but I don't know how to translate that to lua code. I also didn't find a lot of documentation about working with color spaces. For example, which color space are the pixels in after I loaded the jpeg? I'd like to work in a color space that has a good mapping of chroma / hue values to human perception (OKLab / CIELab / HSLuv would be great), how is the support for that in vips / lua-vips? Are there any examples, documentation or tutorials to take a look at to answer these questions?

Thanks again for any help!

jcupitt commented 1 year ago

Hi @heyarne, I'm glad it's useful.

Masking by percent is best done with a cumulative histogram. I would make a histogram, transform it to a cumulative, then search for the 1% and 70% points to get your two thresholds. Use the relational operators (more, less etc.) to make an alpha.

libvips uses the interpretation tag to keep track of how pixel arrays should be interpreted. JPEGs load as either RGB or CMYK, depending on the image type. You can do image:colourspace("lab") to transform whatever it is to CIELAB.

jcupitt commented 1 year ago

... I made a tiny demo prog:

#!/usr/bin/luajit

vips = require "vips"

local image = vips.Image.new_from_file(arg[1])

-- find the normalised cumulative histogram from the monochrome image
-- we could use the RGB image, but we'd get an alpha for each channel, and we
-- want just one alpha
local mono = image:colourspace("b-w")
local hist = mono:hist_find():hist_cum():hist_norm()

-- find the threshold which "percent" pixels are brighter than
function threshold_percent(hist, percent)
    -- build the column and row profiles
    local col, row = hist:more(percent / 100 * hist:width()):profile()

    return row:avg()
end

local low = threshold_percent(hist, 1)
local high = threshold_percent(hist, 70)
print("1% thresh is", low)
print("70% thresh is", high)
local alpha = mono:more(low):andimage(mono:less(high))

-- to CIELAB and add the alpha
image = image:colourspace("lab"):bandjoin(alpha)

image:write_to_file(arg[2])

I can run it like this:

$ ./try341.lua ~/pics/nina.jpg x.v
1% thresh is    9
70% thresh is   178

You need a file format that can handle float CIELAB with alpha, of course. I can view with vipsdisp:

image

rrrnld commented 1 year ago

that's all i needed, thanks!