Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

turn a picture to black and white #243

Closed
floodico opened this issue Jul 17, 2020 · 9 comments
Closed

turn a picture to black and white #243

floodico opened this issue Jul 17, 2020 · 9 comments
Labels

Comments

@floodico
Copy link

Hey there. How can use this to make an bw effect?

@jcupitt
Copy link
Member

jcupitt commented Jul 17, 2020

Hello @floodico,

One way would be:

$ irb
irb(main):001:0> require 'vips'
=> true
irb(main):002:0> x = Vips::Image.new_from_file "k2.jpg"
irb(main):003:0> x = x.colourspace "b-w"
irb(main):004:0> x.write_to_file "x.png"
=> nil

https://libvips.github.io/libvips/API/current/libvips-colour.html#vips-colourspace

@angstyloop
Copy link

This might be helpful. Did I do this right @jcupitt? Am I doing anything silly?

Black and white thresholding with VIPS in C.

https://gist.github.com/angstyloop/37d4454442beea452b718bb11469f2a4

@jcupitt
Copy link
Member

jcupitt commented Mar 5, 2023

Hi @angstyloop, in fact you can do this with just libvips operators -- you need vips_more_const1():

https://www.libvips.org/API/current/libvips-arithmetic.html#vips-more-const1

I'd also set sequential mode, so maybe (untested):

#include <vips/vips.h>

int main (int argc, char** argv) {
    if (argc != 4) {
        printf("USAGE: %s <THRESHOLD (0-255)> <INPUT_IMAGE> <OUTPUT_IMAGE>\n", 
            argv[0]);
        vips_error_exit(NULL);
    }

    if (VIPS_INIT(argv[0]))
        vips_error_exit(NULL);

    VipsImage *in, *out;

    double threshold = strtod(argv[1], NULL);

    if (!(in = vips_image_new_from_file(argv[2],
        "access", VIPS_ACCESS_SEQUENTIAL,
        NULL)))
        vips_error_exit(NULL);

    if (vips_colourspace(in, &out, VIPS_INTERPRETATION_B_W, NULL))
        vips_error_exit(NULL);
    g_object_unref(in);
    in = out;

    if (vips_extract_band(in, &out, 0, "n", 1, NULL))
        vips_error_exit(NULL);
    g_object_unref(in);
    in = out;

    if (vips_more_const1(in, &out, threshold, NULL))
        vips_error_exit(NULL);
    g_object_unref(in);
    in = out;

    if (vips_image_write_to_file(in, argv[3], NULL))
        vips_error_exit(NULL);
    g_object_unref(in);

    return EXIT_SUCCESS;
}

@jcupitt
Copy link
Member

jcupitt commented Mar 5, 2023

The libvips C API has local objects, which can be a useful way to express something like this more concisely.

If you write:

    VipsObject *context = (VipsObject *) vips_image_new();
    VipsImage **t = (VipsImage **) vips_object_local_array( context, 4 );

That makes a dummy VipsObject (called context) that does nothing. It then makes t, an array of four object pointers which are local to context, meaning that when context is unreffed, all the non-null pointers in t will also be unreffed.

This lets you chain libvips operators, eg.:

#include <vips/vips.h>

int main (int argc, char** argv) {
    if (VIPS_INIT(argv[0]))
        vips_error_exit(NULL);

    if (argc != 4) {
        printf("USAGE: %s <THRESHOLD (0-255)> <INPUT_IMAGE> <OUTPUT_IMAGE>\n", 
            argv[0]);
        vips_error_exit(NULL);
    }

    VipsObject *context = (VipsObject *) vips_image_new();
    VipsImage **t = (VipsImage **) vips_object_local_array( context, 4 );

    double threshold = strtod(argv[1], NULL);

    if (!(t[0] = vips_image_new_from_file(argv[2], "access", VIPS_ACCESS_SEQUENTIAL, NULL)) ||
        vips_colourspace(t[0], &t[1], VIPS_INTERPRETATION_B_W, NULL) ||
        vips_extract_band(t[1], &t[2], 0, "n", 1, NULL) ||
        vips_more_const1(t[2], &t[3], threshold, NULL) ||
        vips_image_write_to_file(t[3], argv[3], NULL)) {
        g_object_unref(context);
        vips_error_exit(NULL);
    }

    g_object_unref(context);

    return EXIT_SUCCESS;
}

It's a bit annoying to have to keep track of all the t[n] images, but it's fine for fewer than maybe about 10, and it does save many lines of copy-paste. Plus that code is guaranteed not to leak image references.

In larger programs, there's usually something already there you can use as context, so you get the the unreffing for free.

@angstyloop
Copy link

This is legit @jcupitt thanks so much!

@angstyloop
Copy link

angstyloop commented Mar 6, 2023

For anyone reading, interested in the BW thresholding in C, and scratching their heads at vips_more_const ...

It's clear what it is from the docs, which are excellent. But if you just want a summary here - it literally means "true iff the pixel value is greater than the given constant".

In other words, it is a step function of height one, where you get to pick the position of the jump discontinuity.

Digging into the other operators, I see lots of other cool ways to create "boolean" images like the one described. "Thresholding" images with VIPS in C is really easy, once you know how to use these simple relational operations.

VIPS just doesn't use the word "thresholding" for this - that means something different in VIPS land. I don't think there is really a term VIPS uses to describe these, but I've seen them referred to as "boolean" images which is why I adopted that term.

Note: VIPS uses the word "more" instead of "greater", which I like.

https://www.libvips.org/API/8.6/libvips-arithmetic.html#vips-more-const1
https://www.libvips.org/API/8.6/libvips-arithmetic.html#VIPS-OPERATION-RELATIONAL-MORE:CAPS

Update:

I actually needed to use vips_moreeq_const1 instead, to get the behavior I wanted. The pixels that are more than or equal to the threshold should be black, and the pixels that are less than the threshold should be white. Using vips_more_const1, the program behaves correctly except when the threshold is equal to zero. The desired behavior there is all white pixels, but in the previous examples using vips_more_const1, they were black, which makes sense. This is just the typical < vs <= DOH! moment.

@angstyloop
Copy link

angstyloop commented Mar 6, 2023

Also, I learned about sequential access. Here is an excerpt about it from the VIPS docs:

Sequential access

This a fairly recent addition to libvips and is a hybrid of the previous two.

Imagine how this command might be executed:

$ vips flip fred.jpg jim.jpg vertical
meaning, read fred.jpg, flip it up-down, and write as jim.jpg.

In order to write jim.jpg top-to-bottom, it’ll have to read fred.jpg bottom-to-top. Unfortunately libjpeg only supports top-to-bottom reading and writing, so libvips must convert fred.jpg to a random access format before it can run the flip operation.

However many useful operations do not require true random access. For example:

$ vips shrink fred.png jim.png 10 10
meaning shrink fred.png by a factor of 10 in both axes and write as jim.png.

You can imagine this operation running without needing fred.png to be completely decompressed first. You just read 10 lines from fred.png for every one line you write to jim.png.

To help in this case, libvips has a hint you can give to loaders to say “I will only need pixels from this image in top-to-bottom order”. With this hint set, libvips will hook up the pipeline of operations directly to the read-a-line interface provided by the image library, and add a small cache of the most recent 100 or so lines.

There’s an extra unbuffered sequential mode where vips does not keep a cache of recent lines. This gives a useful memory saving for operations like copy which do not need any non-local access.

https://www.libvips.org/API/8.6/How-it-opens-files.md.html

You get an opportunity to use VIPS_ACCESS_SEQUENTIAL, e.g., as the "access" optional argument to the vips_image_new_from_file function.

@angstyloop
Copy link

I was able to compile and run the first revised example from above:

https://gist.github.com/angstyloop/7d14dcd9b0bdc57cffb849282a74c354

@angstyloop
Copy link

I was able to compile and run the second revised example from above:

https://gist.github.com/angstyloop/38aa2ceca01b592f4ca358f58d0ae3e0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants