libvips / lua-vips

Lua binding for the libvips image processing library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Using VIPs library and Lua to process pictures

hzm199822 opened this issue · comments

To get an original picture from the file, what I want to do is to select a position, cut a rectangle, and make a concave or convex semicircle on the edge of the rectangle, and make this area transparent. Is there an appropriate API or solution?

Hi @hzm199822, could you post a sample image showing what you want?

For example, this:

#!/usr/bin/luajit

local vips = require "vips"

local im = vips.Image.new_from_file(arg[1], {access = "sequential"})

-- make a transparent square ... just RGBA all 0 (black)
local square = vips.Image.black(64, 64, {bands = 4})

-- insert into the main image
im = im:insert(square, 100, 100)

im:write_to_file(arg[2])

makes a square in an image transparent, eg.:

$ ./try320.lua ~/pics/lion.png x.png

x

Where do you need the semicircle? Do you need a line, or a segment?

What I want is not all white, but translucency. Or that piece becomes a watermark

What I want is that this square is just a benchmark. I want to add a semicircle or subtract a semicircle on any two sides of the four sides of the square. Of course, the diameter of the semicircle must be smaller than the side length

What I want is that this square is just a benchmark. I want to add a semicircle or subtract a semicircle on any three sides of the four sides of the square. Of course, the diameter of the semicircle must be smaller than the side length.
The translucency on the right side of the image can also be a fuzzy watermark. Then the block on the left side, I hope, is the same as the missing block in the original image, which is equivalent to providing a picture. I need to make two images, one is with watermark, the other is a small piece taken out separately
I don't know if luavips can do this for images (add or subtract semicircles on the edges of rectangles)

The square in the lion only looks white because the page background is white. Try opening the image in a new tab.

You can overlay complex shapes with composite. You give it an RGBA image, one of the PDF blending modes (eg. over) and a position.

Could you upload some test images? It would save me making them. Just drag the images into the text box.

Composite example:

#!/usr/bin/luajit

vips = require 'vips'

local image = vips.Image.new_from_file(arg[1], {access = "sequential"})
local overlay = vips.Image.new_from_file(arg[2], {access = "sequential"})

-- composite the overlay at 50, 20 using 'over' blend mode
image = image:composite(overlay, "over", {x = 50, y = 20})

image:write_to_file(arg[3])

Generates:

x

I mainly want to extract a figure from the original image, then save it, and change the extracted part of the original image into translucent or other

It's important to extract this step, but it seems to me that the API can only extract simple rectangles

You can apply masks if you want more complex shapes.

Post a set of sample images and I'll have a go.

Use the github website and drag images into the text box.

You need to wait for the upload to finish before pressing Comment.

OK, now it's OK. My goal is very simple, that is to get such a shape from the original image rather than just a simple rectangle. After saving this shape, I will do some operations on the original image, such as translucency, or other things. I can now extract rectangles and darken them myself, but I don't know if there is such an API to support my goal.

Ah, like a jigsaw, I wondered.

Yes, it should be easy. Do you have that shape as a PNG?

What I want to do is JPG, the encoding format of JPG is simpler than PNG, and the operation time should be shorter

I mean, do you have a PNG for the jigsaw piece? It would save me drawing it.

like this?

I found this sample:

jigsaw

Example code:

#!/usr/bin/luajit

vips = require 'vips'

local image = vips.Image.new_from_file(arg[1], {access = "sequential"})
local mask = vips.Image.new_from_file(arg[2], {access = "sequential"})

-- the position we place the piece
left = 200
top = 100

-- get just the alpha from the mask
alpha = mask:extract_band(3)

-- image pixels
rgb = image:crop(left, top, mask:width(), mask:height())

-- brighten 50%
rgb = rgb * 1.5

-- attach the mask as the alpha channel
overlay = rgb .. alpha

-- composite back
image = image:composite(overlay, "over", {x = left, y = top})

image:write_to_file(arg[3])

I can run like this:

$ ./try320.lua ~/pics/Gugg_coloured.jpg ~/pics/jigsaw.png x.jpg

To make this:

x

no...

What I want to do is to get a jigsaw block from the original image, not a rectangle, because I need this puzzle block to do some operations. This jigsaw block comes from the original picture, and the position obtained from the original image is different each time

Sorry, I still don't understand what you want to do.

Are you trying to hide or remove the watermark?

local vips = require"vips.vips"

local background = "/home/hzm/hzm/vips/8.jpg"
local back = vips.Image.new_from_file(background)
math.randomseed(tonumber(tostring(os.time()):reverse():sub(1,7)))
local s = os.clock()
local random_left = math.random(512,914)
local random_top = math.random(1,626)
local random_length = math.random(81,100)
local square_black = vips.Image.black(random_length,random_length,{bands = 3})

local square_back = vips.Image.crop(back,random_left,random_top,random_length,random_length)
back = back:insert(square_black,random_left,random_top)
back:write_to_file("/home/hzm/hzm/vips/background.jpg")
square_back:write_to_file("/home/hzm/hzm/vips/square_back.jpg")
local e = os.clock()
print("used time:"..e-s.."seconds")

thsi api crop Help me get a rectangle from the original, but I want to get a puzzle block instead of a rectangle. What should I do? I've thought about getting rectangles and two semicircles, but I haven't found the API for getting semicircles

You just get a rectangle and then apply an alpha to cut the exact shape, like in my example above.

I don't think I understand this API properly :extract_band

Example:

#!/usr/bin/luajit

vips = require 'vips'

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

-- the position we take the piece from
from_left = math.random(0, image:width() - mask:width())
from_top = math.random(0, image:height() - mask:height())

-- the position we put the piece back
from_left = math.random(0, image:width() - mask:width())
from_top = math.random(0, image:height() - mask:height())

-- get just the alpha from the mask
alpha = mask:extract_band(3)

-- image pixels
pixels = image:crop(from_left, from_top, mask:width(), mask:height())

-- brighten and darken 50%
brighter = pixels * 1.5
darker = pixels * 0.5

-- attach the mask as the alpha channel
brighter = brighter .. alpha
darker = darker .. alpha

-- composite back
image = image:composite(darker, "over", {x = from_left, y = from_top})
image = image:composite(brighter, "over", {x = to_left, y = to_top})

image:write_to_file(arg[3])

To make:

x

I expect you'd like a shadow as well. The easiest way is to draw the shadow on the mask, but you could also compute one with a convolution.

If you have a three-band image (eg. RGB) you can extract a band like this:

g = rgb:extract_band(1)

Now g is a one-band image (just the green). Bands number from zero.

I'd like to know how you changed the rectangle into this puzzle. Instead of drawing this puzzle in advance, you took it directly from the original picture

You mean, let me get a rectangle from the original picture and then turn it into a puzzle?

The most important thing is to get it directly from the original drawing, not draw it yourself.

I just took the alpha channel from that jigsaw PNG.

You can draw yourself, of course, if you have something like an SVG of the shape.

Of course, I know that I can draw by myself, but the effect I want is to take it directly from the original image, because the shape and content are different each time. Do I have to draw again every time? Scholar, can you understand what I mean?

local vips = require"vips.vips"

local background = "/home/hzm/hzm/vips/8.jpg"
local back = vips.Image.new_from_file(background)
math.randomseed(tonumber(tostring(os.time()):reverse():sub(1,7)))
local s = os.clock()
local random_left = math.random(512,914)
local random_top = math.random(1,626)
local random_length = math.random(81,100)
local square_black = vips.Image.black(random_length,random_length,{bands = 3})

local square_back = vips.Image.crop(back,random_left,random_top,random_length,random_length)
back = back:insert(square_black,random_left,random_top)
back:write_to_file("/home/hzm/hzm/vips/background.jpg")
square_back:write_to_file("/home/hzm/hzm/vips/square_back.jpg")
local e = os.clock()
print("used time:"..e-s.."seconds")

I don't believe it. You don't understand what I need

Instead of

local square_back = vips.Image.crop(back,random_left,random_top,random_length,random_length)

You can write:

local square_back = back:crop(random_left,random_top,random_length,random_length)

No, I don't understand what you mean :-(

Do you want to detect a jigsaw shape in the original image somehow? Do you know where it is, or do you need to search for it? Is it always the same shape, or do you need to detect any kind of jigsaw shape?

If you can give a clear explanation of what you need, perhaps I can help.

The original image is a normal 320 * 160 rectangle. I want to cut the next puzzle shape from the original image. I don't know where I need to cut, because I can use random numbers to determine the coordinates I want to cut. I define their shape as a rectangle and two semicircles, which can be concave or convex. I need to cut it off from the original.

You know, VIPs provides cuts that I can only find clipping rectangles

If there is a semicircle, I think I can cut it twice. The first time I cut the rectangle, the second time I cut two semicircles on the edge of the rectangle, and then I can cut the combination again to achieve the desired effect

When I combine these parts, he is a puzzle shape. I then turn the puzzle into black or white or transparent, and put it on the original picture. That's all right

If you still can't understand, forget it

Thank you for your patience

I appreciate it

Maybe like this?

#!/usr/bin/luajit

vips = require 'vips'

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

-- for a size x size square, return one of the four edge points randomly
function random_edge(size)
    local edges = {
        {size / 2, 0},
        {size, size / 2},
        {size / 2, size},
        {0, size / 2}
    }
    
    return unpack(edges[math.random(0, 3)])
end 

-- generate a mask ... a square with a semicircle removed from one edge and
-- added to another
local size = 128
local radius = size / 3
local black = vips.Image.black(size + 2 * radius, size + 2 * radius)
local mask = black:draw_rect(255, radius, radius, size, size, {fill = true})
local left, top = random_edge(size)
mask = mask:draw_circle(255, radius + left, radius + top, radius, {fill = true})
local left, top = random_edge(size)
mask = mask:draw_circle(0, radius + left, radius + top, radius, {fill = true})

-- the position we cut the piece from
left = math.random(0, image:width() - mask:width())
top = math.random(0, image:height() - mask:height())

-- crop out the piece and mask
crop = image:crop(left, top, mask:width(), mask:height()) .. mask
crop:write_to_file(arg[2])

-- make a transparent piece to draw on the image
transparent = (black .. black .. black .. mask):copy{interpretation = "srgb"}
image = image:composite(transparent, "dest-out", {x = left, y = top})
image:write_to_file(arg[3])

Then:

$ ./try320.lua ~/pics/Gugg_coloured.jpg x.png y.png

Makes these two images:

x

y

Yeah, that's what I mean

How to get rid of the black part of the block to get the puzzle inside

Hello again, you need to save as PNG -- jpg images don't support transparency.

图片
I see. I mean, this is the picture you got, but there are still white edges around it. All I need is the inner part. How to remove the white edge of this part, because I need to put this picture back into the original image, and the white edge will be redundant.

No, the edges are transparent. Try opening this in a new tab:

x

I see:

x

No, he's not transparent. He has colors. white

What I want is a small image with no edges, not one with white or black edges

I want to know if there is an API that can cut the edges of a puzzle directly, just a part in the middle

I promise you, there is no white border. Are you converting to JPG at some point? That will remove the transparency.

Download this PNG image (x.png):

x

Now enter this:

vips composite2 Gugg_coloured.jpg x.png result.jpg over --x 100 --y 100

That will load Gugg_coloured.jpg, then composite x.png (the jigsaw piece) into the image and save as result.jpg. You will see this:

result

No border.

Do not use JPG for images with transparency. JPG does not support transparent elements.

图片
Put PNG on top of JPG to get a JPG

But this is only the VIPs API. What I need to do is JavaScript to do this operation. I don't know whether it supports putting PNG in JPG to get jpg

Only in this way? Or I use PNG for my original image

When I generated these images, I saved them all in PNG format. Finally, the white edge can be eliminated by combining the proposite2 function, but I don't know if JS can do the same

Yes, you can use CSS blending modes to layer images, eg.:

https://getflywheel.com/layout/css-blend-modes/

Just curious, are you trying to create a jigsaw puzzle out of an image? If so, the SVG loader of libvips could be useful here.

For example, take this zebra image: zebra.jpg

And this code:

#!/usr/bin/luajit

local vips = require "vips"

local filename = "images/zebra.jpg"
local paths = {
    "<path transform=\"translate(56,0)\" d=\"M0,0 L206,0 C198,17 222,78 214,80 C157,37 160,173 221,130 C235,132 215,223 229,240 C213,225 153,244 152,230 C196,168 92,165 106,221 C105,228 16,203 0,211 L0,0\"/>",
    "<path transform=\"translate(45,0)\" d=\"M217,0 L475,0 C460,17 478,92 462,94 C399,66 395,171 450,144 C457,145 430,221 437,238 C425,249 369,227 371,238 C418,298 318,298 335,239 C338,228 252,251 240,240 C226,223 246,132 232,130 C171,173 168,37 225,80 C233,78 209,17 217,0\"/>",
    "<path transform=\"translate(-28,0)\" d=\"M548,0 L757,0 C767,13 743,85 753,84 C811,52 810,154 749,123 C737,121 758,193 746,206 C729,199 654,224 652,217 C680,161 575,165 603,227 C602,242 527,223 510,238 C503,221 530,145 523,144 C468,171 472,66 535,94 C551,92 533,17 548,0\"/>",
    "<path transform=\"translate(-122,0)\" d=\"M851,0 L1066,0 L1066,235 C1051,221 991,240 991,226 C1034,163 930,160 945,216 C944,224 855,199 840,206 C852,193 831,121 843,123 C904,154 905,52 847,84 C837,85 861,13 851,0\"/>",
    "<path transform=\"translate(56,-66)\" d=\"M0,277 C16,269 105,294 106,287 C92,231 196,234 152,296 C153,310 213,291 229,306 C214,319 233,391 219,389 C157,327 153,488 209,427 C217,425 192,497 200,510 C187,521 116,498 118,509 C180,567 20,567 81,507 C84,495 12,517 0,506 L0,277\"/>",
    "<path transform=\"translate(45,-66)\" d=\"M240,306 C252,317 338,294 335,305 C318,364 418,364 371,304 C369,293 425,315 437,304 C426,318 448,392 437,392 C378,363 378,466 437,437 C448,437 426,511 437,526 C422,535 362,511 362,520 C406,578 302,576 316,515 C315,502 226,523 211,510 C203,497 228,425 220,427 C164,488 168,327 230,389 C244,391 225,319 240,306\"/>",
    "<path transform=\"translate(3,-66)\" d=\"M479,304 C496,289 571,308 572,293 C544,231 649,227 621,283 C623,290 698,265 715,272 C726,289 705,351 717,353 C777,311 777,446 719,404 C709,406 732,497 721,514 C704,527 628,506 626,518 C653,579 547,580 575,522 C572,512 496,535 479,526 C468,511 490,437 479,437 C420,466 420,363 479,392 C490,392 468,318 479,304\"/>",
    "<path transform=\"translate(-108,-66)\" d=\"M826,272 C841,265 930,290 931,282 C916,226 1020,229 977,292 C977,306 1037,287 1052,301 L1052,521 C1037,531 978,509 979,519 C1023,577 920,577 935,517 C935,505 847,526 832,514 C843,497 820,406 830,404 C888,446 888,311 828,353 C816,351 837,289 826,272\"/>",
    "<path transform=\"translate(56,-170)\" d=\"M0,610 C12,621 84,599 81,611 C20,671 180,671 118,613 C116,602 187,625 200,614 C215,629 197,718 212,718 C275,703 279,806 223,762 C216,762 242,821 235,836 L0,836 L0,610\"/>",
    "<path transform=\"translate(11,-170)\" d=\"M245,614 C260,627 349,606 350,619 C336,680 440,682 396,624 C396,615 456,639 471,630 C482,643 460,700 471,698 C530,652 530,784 471,738 C460,736 482,823 471,836 L280,836 C287,821 261,762 268,762 C324,806 320,703 257,718 C242,718 260,629 245,614\"/>",
    "<path transform=\"translate(-42,-170)\" d=\"M524,630 C541,639 617,616 620,626 C592,684 698,683 671,622 C673,610 749,631 766,618 C755,633 778,706 767,706 C708,676 708,779 768,749 C779,748 757,822 768,836 L524,836 C535,823 513,736 524,738 C583,784 583,652 524,698 C513,700 535,643 524,630\"/>",
    "<path transform=\"translate(-75,-170)\" d=\"M799,618 C814,630 902,609 902,621 C887,681 990,681 946,623 C945,613 1004,635 1019,625 L1019,836 L801,836 C790,822 812,748 801,749 C741,779 741,676 800,706 C811,706 788,633 799,618\"/>"
}

local main = vips.Image.new_from_file(filename)

for i, path in pairs(paths) do
    local svg = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"" .. main:width() .. "\" height=\"" .. main:height() .. "\">"
    svg = svg .. path ..  "</svg>"

    local im = vips.Image.new_from_buffer(svg, "", {access = "sequential"})

    -- Composite the two images using the PDF "dest-in" mode
    local piece = main:composite(im, "dest-in")

    -- Search for the bounding box of the non-background area
    local left, top, width, height = piece:find_trim({
        threshold = 10,
        background = { 0, 0, 0 },
    })

    -- And crop the jigsaw piece
    if width > 0 and height > 0 then
        piece = piece:crop(left, top, width, height)
    end

    print("writing piece" .. i .. ".png")
    piece:write_to_file("piece" .. i .. ".png")
end

To make these jigsaw pieces:

Details

piece1
piece2
piece3
piece4
piece5
piece6
piece7
piece8
piece9
piece10
piece11
piece12

There are a couple of jigsaw puzzle generators to produce these SVG paths. For example:
https://draradech.github.io/jigsaw/index.html

The only drawback of that version is that it combines all SVG paths into a single large path. If you are looking for a simplified generator with the paths separated, you could try this one:
https://kleisauke.nl/jigsaw-generator/

filename=/jpg/bg_2.jpg

I use
local image = vips.Image.new_ from_ file(filename)
however
vips/Image_ methods.lua:137 : VipsForeignLoad: "/jpg/bg_ 2.jpg" is not a known file format

image

image

the type(name)is cdata
Explain that the problem is
vips_ foreign_ find_ load

image
So what's the problem?

Perhaps you've not built libvips with JPEG support, or perhaps you're not passing the correct path. "/jpg/bg_ 2.jpg" looks wrong, for example, unless you really do have a directory called /jpg.