Relative imagemagick transforms: Relative Crop? - imagemagick

I'm trying to execute the following set of transformations without knowing the absolute width or height of the target image:
1. Crop image A by 10px on every size :: A1
2. Create duplicate of A1 translating image by 1270px on the y-axis :: A2
3. Create montage of A1 && A2 :: A3
4. Translate A3 by 385px on the y-axis :: A4
5. Crop A4 at 100% width, and 1270px tall (box from 0,0 to WIDTH,1270) :: A5
My issue is how do I do steps 1 and 5 with relative coordinates? Below are my steps that I've come up with:
1. convert A.jpg -shave 10x10 A1.jpg
2. convert A1.jpg -page +0-1270 -background none -flatten A2.jpg
3. montage A1.jpg A2.jpg -geometry +0+0 A3.jpg
4. convert A3.jpg -page +0-385 -background none -flatten A4.jpg
5. convert A4.jpg ????? A5.jpg

Updated Answer
Ok, I think I understand what you want a bit better now. Here's how I would do it:
convert start.jpg -crop +0+383 +repage \
\( -clone 0 -crop x1270+0+0 \) \
\( -clone 0 -crop x1270+0+1270 \) \
-delete 0 +append result.jpg
The first line says..."Take the starting image and crop off the top 383 pixels and reset all the sizes to match what is left. Call that the first image from now onwards."
The second line says..."To one side (because of the parentheses), clone the first image and crop out the full width and 1270 pixels in height from the top. Hold onto that till later - i.e. keep it in the image list.".
The third line says..."To one side (because of the parentheses), clone the first image, and crop out a piece the full width and 1270 pixels in height, but start 1270 pixels from the top. Hold onto that till later - i.e. keep it in the image list."
The last line says..."Delete the initial image, and then take the two slices in the image list and append them side-by-side, Save as result.jpg".
Original Answer
I think we will need to work together on this one, but it can be done. Let's start with a concrete image that is a 400x250 gradient with a 15px black border:
convert -size 250x400 gradient:red-cyan -rotate 90 -bordercolor black -border 15 A.png
So, step 1.
convert A.png -shave 10x10 A1.png
Step 2&3. I don't get the point of these! You appear to be trying to add transparent space to a JPEG which doesn't support transparency. Also, I can't tell what you are montaging where. Please try and express what you want with these two steps in plain English, like "add a transparent area N pixels wide/tall above/below, left/right of A2", or maybe "add a transparent area such that the new dimensions are X,Y and the original A2 image is at the bottom-right of the new canvas."
If I have a stab at steps 2&3, I'll guess this (and add a purple border so you can see it on StackOverflow's white background). I am appending a transparent area 1270 pixels tall below the image.
convert A.png -background none -shave 10x10 xc:none[1x1270\!] -append b.png
Step 4&5. As above.
Or maybe you mean this:
convert A.png -background magenta -shave 10x10 -gravity southeast -extent 1270x385 result.png
I think we can get you down to a single command, with no intermediate files, if we understand your needs.

Related

Black line between 2 images while using append on imagemagick

I have two files;
1.jpg = 14000x2800 pixels
2.jpg = 2800x128 pixels
I use the following command:
...\convert.exe -auto-orient -quality 100 -append "1.jpg" "2.jpg" "out.jpg"
The goal is to merge the images together on the vertical axis, and it works great but now I need to add a straight horizontal black line between images (line's length should be the biggest file's length, meaning from left to right) while appending.
Is there any way to do that without a second command?
Easier description with MSPaint skills
Here's a way to do it:
magick 1.jpg 2.jpg -size "%[fx:u.w>v.w?u.w:v.w]x10" xc:black -swap 2,1 -append result.png
That says... load 1.jpg and 2.jpg. Define the width for the canvas we create next as "whatever is the wider of 1.jpg (referred to as u) and 2.jpg (referred to as v)" by 10 pixels tall. Create a black canvas that size. Now swap the order so that the canvas we just created is between the two images. Append all 3 images and save.
That gives you one of these, depending which one you load first:
If you want the unfilled, white background area beside the narrower image to be, say magenta, use:
magick -background magenta 1.jpg 2.jpg -size "%[fx:u.w>v.w?u.w:v.w]x10" xc:black -swap 2,1 -append result.png
Keywords: ImageMagick, image processing, wider, widest, taller, tallest, match existing image width, match height.

Image rotation relative to wrong (top right) point

I am trying to place a rotated image onto a blank canvas.
This is the command i am using:
convert "(" -size 1000x1000 xc:transparent ")" "(" "img.jpg" -virtual-pixel white -rotate -10.75 ")" -geometry +136+148 -composite "overlay.png" -geometry +0+0 -composite out.png
I would expect the image to appear at +136+148 rotated around its center. However, it appears to be rotated around its upper right corner instead.
I confirmed this in Photoshop:
This is the resulting image (using slightly different size but same parameters as my example command given here):
The 4 lines are the guides that mark the coordinates that define where the image sits. As you can see, the image is rotated -10.75 degrees. And the rotation center is the upper right corner.
Here i select the box at the correct coordinates in Photoshop:
And here i rotate it after setting the rotation point to the top right:
As you can see, the selection now matches the image as it was calculated by my convert command.
I have tried dealing with this for many hours. I tried switching to +distort and -distort and what not, but there i simply get either a cropped image or another wrongly positioned bounding box.
I don't know how to address this problem. What am i doing wrong? How can i tell -rotate to rotate my image around its center, not around the top right corner (why top right anyway?).
Thank you for your help!
The default gravity for offsetting is the northwest corner or top-left corner. The geometry offset you are using moves the top left corner of the padded rotated image after its background has been added and not the top left corner of the where the Input gets rotated. So I think that may be why you are not getting the expected result. ImageMagick -rotate does rotate about its center, but it fills the background out to make a complete rectangular image that is the bounding rectangle about the rotated image.
In ImageMagick, a better approach to what you did would be to use -gravity center and align the center of the rotated input image with the center of the transparent region in the overlay image.
Alternately, measure the 4 corners of your transparent region in the overlay and 4 corners of the input image and do a perspective or affine distortion to align the two.
Here is how to do it in bash unix syntax.
over1="122,186"
over2="466,118"
over3="510,345"
over4="166,411"
WxH=`convert -ping "Image.jpg" -format "%wx%h" info:`
ww=`echo "$WxH" | cut -dx -f1`
hh=`echo "$WxH" | cut -dx -f2`
ww=$((ww-1))
hh=$((hh-1))
in1="0,0"
in2="$ww,0"
in3="$ww,$hh"
in4="0,$hh"
convert overlay.png \
\( Image.jpg -virtual-pixel none +distort Perspective \
"$in1 $over1 $in2 $over2 $in3 $over3 $in4 $over4" \) \
-layers merge +repage \
out.png
See https://imagemagick.org/Usage/distorts/#perspective
Try using the offset geometry that aligns with the outer edges of your target area. That would be more along the red lines I've added here, not along the cyan lines in your example image.
Edited to add: For a method of finding the proper offset information to fill the hole and placing the image under the hole, see my other answer in this thread.
You can find the location of the cutout in your overlay image, then use that information to properly place your under image with a command like this...
convert overlay.png -background none \
\( +clone -alpha extract -trim \
-set option:offset +%[fx:page.x]+%[fx:page.y] +delete \) \
\( image.jpg -rotate -10.7 -set page %[offset] \) \
-set page %[fx:u.w]x%[fx:u.h] +swap -layers merge result.png
This makes use of the fact that the bounding box dimensions of the rotated input image are the same as the bounding box dimensions of the cutout of the transparent region in the overlay image.
That reads in the overlay, and inside parentheses it makes a clone of it, extracts the alpha channel, and trims the result to find the offset of the cutout. It saves the offset to a variable named "offset".
Then inside another set of parentheses it reads in the under image, rotates it the known -10.7 degrees, and uses that variable "offset" to set the paging offset on the rotated "image.jpg".
Then outside the parentheses it uses the width and height of the overlay to set the paging dimensions for the under image.
It finishes by swapping the order of the images to put the overlay on top, then merging the two layers to create the output.
Here is an alternate to GeeMack's excellent ImageMagick solution. This computes the center of the original input.jpg, and the center of the transparent region in the Overlay.png. Then it uses +distort SRT to translate and rotate the image before finally compositing with -layers merge.
convert Overlay.png \
\( +clone -alpha extract -trim -set option:center "%[fx:page.x+w/2],%[fx:page.y+h/2]" +delete \) \
\( Image.jpg -virtual-pixel none +distort SRT "%[fx:w/2],%[fx:h/2] 1 -10.7 %[center]" \) \
+swap -layers merge +repage result2.png
The second line is much line GeeMack's, but it computes the center of the transparent region's bounding box and saves that in the "center" argument and then deletes the trimmed image.
The third line computes the center of the Input.jpg image (before rotation). It then uses +distort SRT to translate from the input center, (scale=1) and rotate -10.7 and translate to the center of the transparent region's bounding box.
See https://imagemagick.org/Usage/distorts/#srt

Remove lines of particular size from image

I have used the following imagemagick command for the image below:
convert img.png -define morphology:compose=darken -morphology Thinning Rectangle:17x1+0+0\< tmp.png
This removes ALL lines from the image, but I just want to remove the small horizontal and vertical lines on the right and bottom of the number in top left corner of each block. I want to preserve the main column and row lines. Can anyone tell me how to do it?
This is what I get (notice the long lines dividing the image content into columns and rows are also gone. I want those line to stay):
I notice the script is finer/thinner and less regular than the lines so it is more susceptible to eroding techniques. With that in mind we can ditch the text like this:
convert vcards.png -colorspace gray -threshold 50% -morphology erode disk:1.5 +repage z1.png
That's a good start, but if we use that as a mask, we will lose the long horizontal lines in your original image. So, we can find all those by projecting all the rows into a single-pixel wide tall column and thresholding all the rows that are more than 80% white. Then widen the image back out to its original width.
convert z1.png -colorspace gray -resize 1x\! +repage -threshold 80% -scale 810x1518\! +repage z2.png
Now combine the two masks so they only do the lower and right sides of your little title box things.
convert z1.png \( z2.png -negate \) -compose darken -composite z3.png
Finally, fatten that mask up a bit because it may have shifted around during previous processing, and apply it to your original image.
convert vcards.png \( z3.png -morphology dilate disk:2 -negate \) -compose darken -composite result.png
It could all be combined into a single command, but I won't do that, because some aspects may not work for all your images and while they are all individually implemented and documented, they are simpler to improve or correct individually.
you can use the followin code for horizontal an change it slightly for vertical lines
magick 21.gif -monochrome ( +clone -negate -statistic median 219x1 ) -compose lighten -composite q1.png

How can I overlay a larger watermark/frame image on a smaller one?

I have an overlay image, which is like a watermark/logo, which needs to be overlayed on top of the source image (while preserving alpha channel, etc)
When overlay is the same or smaller dimension as the source image - things are easy:
composite.exe -alpha on -gravity center logo.png in_image.jpg out_image.jpg
However, when logo.png is larger than in_image.jpg - above call truncates the logo, and out_image.jpg has the same dimensions as in_image.jpg
I would like the resulting image to be the largest of either the logo.png or in_image.jpg so I can do things like artistic frames around the photos.
Below image demonstrates the end result I want to be able to get this:
Desired Result
Note, here, the png with skulls has larger dims than the kiddo's image. The alpha channel needs to be preserved.
Edit: more clarity through examples
Here is another desired result
Here, the png file is opaque on the sides, has a clear window in the middle, and half-translucent bubbles. the JPG file is just a regular JPG from a camera.
Would love to add original and logo files that result in it, but lack reputation to add more than 2 links (or to add images)
Updated Answer
If you have v7 of ImageMagick, you can get it to do the maths for you all in one line using -fx to determine the dimensions of the larger of the two images:
magick background.jpg overlay.png -background none -gravity center -extent '%[fx:u.w>v.w?u.w:v.w]x%[fx:u.h>v.h?u.h:v.h]' -composite result.png
That basically says... "Extend the two images as follows. If the width of the first image (u.w) is greater than that of the second image (v.w), then use the width of the first, else use that of the second. Same for height.".
Original Answer
I believe you want this. Get width of whatever is wider of background and overlay. Get height of whatever is taller of background and overlay. Extend both background and overlay with transparent pixels to new dimensions. Overlay.
So, if we start with this as background (300x50):
And this as overlay (122x242) - which is a tall blue rectangle surrounded with transparency then that is bordered in black to show the extent of it:
You would run this, which is actually very simple but it is full of comments and debug output so you can see what is going on:
#!/bin/bash
# Get background width and height
read wb hb < <(convert background.jpg -format "%w %h" info: )
echo "Background: " $wb $hb
# Get overlay width and height
read wo ho < <(convert overlay.png -format "%w %h" info: )
echo "Overlay: " $wo $ho
# Get wider of the two
w=$wb
[ $wo -gt $w ] && w=$wo
# Get taller of the two
h=$hb
[ $ho -gt $h ] && h=$ho
echo "New dimensions: " $w $h
convert background.jpg overlay.png -background none -gravity center -extent ${w}x${h} -composite result.png
Here is how it looks running:
Background: 300 50
Overlay: 122 242
New dimensions: 300 242
Presumably, when you have done your overlay, you would add -trim as the final part of the command line to remove any extraneous stuff that has been added.
Try using convert rather than composite. It is more flexible than composite.
convert logo.png in_image.jpg -gravity center -compose over -composite out_image.jpg
But if you insist on using composite, then
composite in_image.jpg logo.png -gravity center -compose src_over out_image.jpg
Note sure exactly what you mean by preserving the alpha channel, since jpg does not support transparency. Perhaps you can post a pair of inputs and your desired output, if what I have posted does not do what you want.

Imagemagick montage 3x3 spacing 1px

I want to merge 9 images using imagemagicks convert into 3x3 tiles with a 1px spacing between the tiles.
In my other, bigger version of the resulting image I have a spacing of 2px. To achieve this I've added a border of 1px to every image, and after the image has been created I'm using crop to strip the outer border.
Is there an easy way to get just 1px spacing between the tiles, unfortunately a border of 0.5px doesn't work.
The best.
Your method is similar to that used by the montage program, which has the same limitation that you found. In order to get 1px spacing between tiles, you will need a little bit more involved of a convert command. The method used here first builds the rows of the resulting image, then sticks them together, then cuts out border pixels that we don't want.
convert \( 0.png 1.png 2.png -splice 1x +append \) \
\( 3.png 4.png 5.png -splice 1x +append \) \
\( 6.png 7.png 8.png -splice 1x +append \) \
-splice x1 -append -chop 1x1 \
out.png
In more detail:
Each parenthesized subgroup says to add a column of width 1 to the left side of each image in the subgroup. The first image in the subgroup gets a column added, too, so we will clean this up at the end. We then append the images of our row horizontally, and then do the same for the second and third row.
After building our rows, we use splice to add a 1 pixel row to the top of our three rows, and append vertically.
At this point, we almost have the image we want, but we have a 1 pixel border on the left and on the top that we need to get rid of, so that is what that final -chop is for.
Hope this helps.

Resources