Page 1 of 1

Vertical slices - Array and loop?

Posted: 2015-11-24T02:44:37-07:00
by miguellint
Hello...

I have an image 100x100 pixels.

I am slicing the image into four vertical slices of varying widths.

I want the first slice to be 10 pixels wide, the second slice to be 20 pixels wide, the third slice to be 30 pixels wide and the fourth slice to be 40 pixels wide.

The height is a constant 100% of the image.

I know the following code will do the job...

Code: Select all

convert in.png -crop 10x0+0+0  slice1.png
convert in.png -crop 20x0+10+0 slice2.png
convert in.png -crop 30x0+30+0 slice3.png
convert in.png -crop 40x0+60+0 slice4.png
... but is there a better way.

How can I ask IM to just slice from column 0 to column 10, from columns 10 to 30, from columns 30 to 60, and from column 60 to 100.

---

My reason for asking is that I want to slice a 1000x1000 image into sixty vertical slices of varying widths. I have the sixty x coordinates (0, 15, 37, 58, 84 ... 852, 912, 945, 982) but working out offsets and writing out sixty crop commands seems like the wrong way to go.

I'm guessing an array of x coordinates and some sort of loop is the correct way to go but I can't even work out what command to use as they all seem to need an offset. (Or do I also use the loop to work out the offset?)

Still a programming and IM noob so all assistance appreciated :-)

Kubuntu 15.10
IM 6.8.9-9

Regards
Miguel

Re: Vertical slices - Array and loop?

Posted: 2015-11-24T10:17:52-07:00
by fmw42
Seems to me that you will have to make a list of pairs of arguments (width and offset) for each slice, if the offsets do not follow some fixed pattern. Your example above has width growing by steps of 10, but the offsets seem to have no pattern. So you could make a list of width offset as a file. Then read the file and loop over each line of the file and run a convert for each.

Alternately, you could read the input image once and clone and crop it multiple times writing out the result and deleting the clone. See http://www.imagemagick.org/Usage/files/#write. But that would be one large command line, though you could write it as a bash unix script file and just execute the script.

We can give you the commands for either for your short example and you can expand it for your larger number of slices case.

Re: Vertical slices - Array and loop?

Posted: 2015-11-24T10:36:42-07:00
by Bonzo
You can use variables in your commands for instance:
convert in.png -crop variable1x0+0+0 slice1.png
convert in.png -crop variable2x0+variable1+0 slice2.png

You could have your widths in an array and dynamically create the command; save the command into a variable and use that. Something like:

Code: Select all

 " input.jpg \( -clone 0 -crop variable1x0+0+0 -write slice1.png +delete \) 
" \( -clone 0 -crop variable2x0+variable1+0 -write slice2.png +delete  \) 
There are other similar methods to avoid temporary images and get everything on one command.

Re: Vertical slices - Array and loop?

Posted: 2015-11-25T02:39:14-07:00
by miguellint
Thanks for the replies :-)

I've ended up with the following bash script using the crop command, two loops and two arrays. It seems to work.

Apologies for the overuse of comments and overlong variable names. Still a noob.

Code: Select all

# Draw a striped rectangle
convert  -size 100x100 xc: -fill red -draw "rectangle 0,0,10,100" -fill green -draw "rectangle 10,0,30,100"  -fill yellow -draw "rectangle 30,0,60,100"  -fill blue -draw "rectangle 60,0,100,100"  striped_rectangle.jpg

# Find the width of the striped rectangle
# Needed to calculate the width of the right-most stripe
imageWidth=$(identify -ping -format %w striped_rectangle.jpg)

# Clear array containing x-coordinates and image width
unset x_coords

# Clear array containing width of each stripe
unset stripeWidthArray

#Fill x-coordsArray with x-coordinates and image width
x_coords=(0 10 30 60 $imageWidth) 

# Find the number of elements in the x_coordinates array
x_coordsArrayLength=${#x_coords[@]}

# Loop to calculate width of each stripe
# Append each width to stripeWidthArray
for (( i=0; i < x_coordsArrayLength-1; i++ ))
  do
     stripeWidth=$[${x_coords[$i+1]}-${x_coords[$i]}]
     stripeWidthArray+=($stripeWidth)
  done

# Find the number of elements in the stripeWidthArray
stripeWidthArrayLength=${#stripeWidthArray[@]} 

# Loop to crop original image
for (( i=0; i < stripeWidthArrayLength; i++ ))
do
convert striped_rectangle.jpg -crop ${stripeWidthArray[i]}x0+${x_coords[i]}+0 striped_rectangle_out$i.jpg
done

Any suggestions for reading a text file of x-coordinates directly into the x-coordinates array.

Thanks
Miguel

Kubuntu 15.10
IM 6.8.9-9
GNU bash 4.3.42(1)

Re: Vertical slices - Array and loop?

Posted: 2015-11-25T03:27:39-07:00
by Bonzo
One thing I noticed is you read the original file in for every crop. Using my example you only need to read the image in once.

This is another example - I use mostly use php and this is an example from my website but it will give you an idea how it works - this time the image is read in once and saved into the memory and you then call it up for each crop. Although I tried some tests a while ago and saving to memory was no faster than reading the file in every time. I suppose it depends on the file type, size etc.

Code: Select all

$cmd = "input.jpg -write mpr:image +delete ". 
" \( mpr:image -thumbnail x480 -write 480_wide.jpg \) ". 
" \( mpr:image -thumbnail x250 -write 250_wide.jpg \) ". 
" \( mpr:image -thumbnail x100 -write 100_wide.jpg \) ". 
" \( mpr:image -thumbnail 64x64! -write 64_square.jpg \) ". 
" \( mpr:image -colorspace Gray -write black_white.jpg \)"; 
exec("convert $cmd ");
[/php]

Re: Vertical slices - Array and loop?

Posted: 2015-11-25T10:43:10-07:00
by fmw42
Any suggestions for reading a text file of x-coordinates directly into the x-coordinates array.

Code: Select all

array=(`cat file.txt`)
these are back-tics not single quotes

alternately

Code: Select all

array=($(cat file.txt))

Re: Vertical slices - Array and loop?

Posted: 2015-11-26T20:06:29-07:00
by miguellint
Bonzo wrote:One thing I noticed is you read the original file in for every crop.
Hello Bonzo...

Thanks for the advice but I'm having a fundamental "not understanding what's going on" session.

I understand using the image-in-memory instead of loading the image each time but all I can come up with is throwing the final loop of my original example into the middle of a convert/crop command. Something like...

Code: Select all

convert striped_rectangle.jpg -write mpr:stripedRectangle 

for (( i=0; i < stripeWidthArrayLength; i++ ))
do
mpr:stripedRectangle  -crop ${stripeWidthArray[i]}x0+${x_coords[i]}+0 striped_rectangle_out$i.jpg
done

Error message says mpr:stripedRectangle: command not found

---

Haven't even got to the part about using php instead of bash :-)

Thanks for any advice
Miguel

Re: Vertical slices - Array and loop?

Posted: 2015-11-26T22:48:24-07:00
by fmw42
mpr:stripedRectangle -crop ${stripeWidthArray}x0+${x_coords}+0 striped_rectangle_out$i.jpg


First issue, you do not call convert. Second issue is that mpr: images do not persist after any given convert is finished.

I believe what user bonzo was trying to convey was that if you have a fixed set of arguments for all images, then you can put the arguments into variables and create one convert command to read different strips of the image and write those out.

Unix Syntax:

Code: Select all

convert striped_rectangle.jpg \
\( -clone 0 -crop ${stripeWidthArray[0]}x0+${x_coords[0]}+0 +repage -write striped_rectangle_out0.jpg +delete \) \
\( -clone 0 -crop ${stripeWidthArray[1]}x0+${x_coords[1]}+0 +repage -write striped_rectangle_out1.jpg +delete \) \
\( -clone 0 -crop ${stripeWidthArray[2]}x0+${x_coords[2]}+0 +repage -write striped_rectangle_out2.jpg +delete \) \
...
\( -clone 0 -crop ${stripeWidthArray[N]}x0+${x_coords[N]}+0 +repage -write striped_rectangle_outN.jpg +delete \) \
null:
or

Code: Select all

convert striped_rectangle.jpg -write mpr:img +delete \
\( mpr:img -crop ${stripeWidthArray[0]}x0+${x_coords[0]}+0 +repage -write striped_rectangle_out0.jpg +delete \) \
\( mpr:img -crop ${stripeWidthArray[1]}x0+${x_coords[1]}+0 +repage -write striped_rectangle_out1.jpg +delete \) \
\( mpr:img -crop ${stripeWidthArray[2]}x0+${x_coords[2]}+0 +repage -write striped_rectangle_out2.jpg +delete \) \
...
\( mpr:img -crop ${stripeWidthArray[N]}x0+${x_coords[N]}+0 +repage -write striped_rectangle_outN.jpg +delete \) \
null:
But you need to have a row for each of the N strips you want in the command line. You can put all the arguments for each row in rows of two text files (one for width and the other for x-offsets) and then read them in and put them into your arrays as I mentioned earlier.

See
http://www.imagemagick.org/Usage/files/#write
http://www.imagemagick.org/Usage/basics/#parenthesis
http://www.imagemagick.org/Usage/basics/#clone

For Windows, replace line ending \ with ^ and remove \ from \( and \). See http://www.imagemagick.org/Usage/windows/

Re: Vertical slices - Array and loop?

Posted: 2015-11-27T02:03:15-07:00
by Bonzo
I believe what user bonzo was trying to convey was that if you have a fixed set of arguments for all images, then you can put the arguments into variables and create one convert command to read different strips of the image and write those out.
That is correct fmw42; I am afraid I was just being lazy.

Re: Vertical slices - Array and loop?

Posted: 2015-11-28T13:42:02-07:00
by miguellint
Hello Fred...

Thanks for the examples. Less confused than before but still two questions.

1) I'm guessing that repeating the crop command is a typo as the examples only work when I delete one of the crops...

Code: Select all

-clone 0 -crop  -crop
and

Code: Select all

img -crop  -crop
2) I still can't figure out how to incorporate the loop. Your example is fine for a few crops but when I'm doing sixty crops, as mentioned earlier, it means a lot of typing. Plus it's not always exactly sixty crops. Sometimes a few more or a few less.

More importantly, if I put the convert command into the loop it's simply re-loading the image each cycle which defeats the purpose of the clone/mpr.

Apologies :-) I have a feeling I'm missing something obvious.

Thanks
Miguel

Re: Vertical slices - Array and loop?

Posted: 2015-11-28T14:40:45-07:00
by fmw42
1. You are right. Sorry, my typo. Only one -crop was intended.

2. The command line was only usable in a loop if all images were to be sliced into exactly the same number of slices.

If you did run it in a loop, you must change your image each time. Unix script syntax.

Code: Select all

cd yourdirctory
list=`ls"
for img in $list; do
inname=`convert $img -format "%t" info:`
convert $img -write mpr:img +delete \
\( mpr:img -crop ${stripeWidthArray[0]}x0+${x_coords[0]}+0 +repage -write ${inname}_out0.jpg +delete \) \
\( mpr:img -crop ${stripeWidthArray[1]}x0+${x_coords[1]}+0 +repage -write ${inname}_out1.jpg +delete \) \
\( mpr:img -crop ${stripeWidthArray[2]}x0+${x_coords[2]}+0 +repage -write ${inname}_out2.jpg +delete \) \
...
\( mpr:img -crop ${stripeWidthArray[N]}x0+${x_coords[N]}+0 +repage -write ${inname}_outN.jpg +delete \) \
null:
done

Re: Vertical slices - Array and loop?

Posted: 2015-11-30T17:42:33-07:00
by miguellint
Thanks Fred and Bonzo.

Managed to make sense of it all now :-)

All the best
Miguel