Page 1 of 2

Segmenting an image using a mask

Posted: 2010-12-02T14:12:53-07:00
by dpfeiffer
I have a set of very large images that I need to cut up into 24 smaller images. Fortunately, I know exactly where the "cuts" need to be made on all of them. I know I could use the -crop command and input all the x and y coordinates, but I'm hoping to find a solution that is easy to manage and efficient in the way it uses system resources. If possible, I only want to read the image once from disk.

I've read a little and found some scripts that would split up images based on white space in between the images, but I can't guarantee that there will always be whitespace where the cuts need to happen. I read how these scripts work - specifically Fred's multicrop. It looks like he generates a mask for the image and then uses that mask to split up the image. Well, I already have the mask that I need to split up the image. How can I use that mask to apply the cuts? (These cuts are not uniform - each resulting image will be the same size, but the cuts don't follow any sort of grid.)

I found this forum topic: viewtopic.php?f=1&t=14148 which sounds exactly like what I'm looking to do. Anthony posed this suggestion:

Code: Select all

convert actual_image.png null: mask1.png mask2.png mask3.png \
+matt -compose CopyOpacity -layers composite -trim +repage \
images-%d.png
Which takes a sequence of masks and cuts out the respective images. It looks he also touches on how to take one mask and make a sequence of masks out of it — but here is where I'm getting confused. I don't understand how he's generating the individual masks from the larger mask.

Does anyone have any suggestions on how to take a large image split it into parts? Ideally taking a gif mask and using that to make the cuts? I'd love to hear how others went about doing this.

Thanks!

Re: Segmenting an image using a mask

Posted: 2010-12-02T15:30:40-07:00
by fmw42
can you show an example of your image and the mask or masks you want to use?

Re: Segmenting an image using a mask

Posted: 2010-12-02T22:30:17-07:00
by anthony
Sounds to me a little like making jigsaw pieces.
http://www.imagemagick.org/Usage/advanced/#jigsaw

I have a few sets of such jigsaw images, one piece per image, that together covers an entire image,

For example if mask_*.png are greyscale masks of all the pieces (with a virtual offsets) then I can generate all the pieces using

Code: Select all

   convert mask_*.png -alpha shape null: photo.png -compose In -layers composite pieces_%03d.png
Alternately if you want to incorporate the input name into the output name...

Code: Select all

     convert mask_*.png -set filename:mask %f -alpha shape \
                 null: image.jpg -compose In -layers composite \
                 pieces_%[filename:mask]
See Filename Percent Escapes
http://www.imagemagick.org/Usage/files/#save_escapes

If you like to send me a copy of your set I will work out an exact command for you to use.

Re: Segmenting an image using a mask

Posted: 2010-12-14T10:50:20-07:00
by dpfeiffer
I apologize for posting and disappearing; I had to focus on a different project for a while.

Thanks for the suggestions. Anthony, I looked into utilizing your work with jigsaw pieces, and while definitely a possible solution, it seemed a little complicated for what I need. Let me post two images to give a better idea of what I'm trying to accomplish:

Mask:
Image

Sample Photo:
Image

I want to take the white areas defined by the mask and create new images from those based on what that mask would yield if being used on the sample photo. So, here I would get nine new (smaller) images, all the same size, that are cut out from the sample photo.

Thanks again for the help/suggestions!

Re: Segmenting an image using a mask

Posted: 2010-12-14T11:58:13-07:00
by fmw42
Modify my multicrop script to replace your mask with mine. That is skip the part of my script that gets the mask and replace it with your mask as a second input image. Then let my script finish processing. I think that might work, but am not really sure without further review of the script. Unfortunately, I am not able to spend time on doing that right now, but it is a good idea and I might integrate it into the script later. If I find time, I will report back.

As I recall (without looking more carefully), my script generates a mask similar to yours, then finds each area and uses floodfill to change its color temporarily, then makes the rest black and then the area white, so you have one mask for one area. Then applies that mask to the image so that you have the area of the image surrounded by black, then uses -trim to crop to the non-black region. Then repeats for the next area from the original mask. This was the idea behind the script. Perhaps you can either modify my script or implement this idea in your own script.

Re: Segmenting an image using a mask

Posted: 2010-12-14T18:28:49-07:00
by dpfeiffer
Thanks for the tip @fmw42. I ended up taking your script and building off of it.

I've never written a shell script before so this was a new experience. I'm pasting below my first working version of the script. It's not pretty or user-friendly by any means, but I probably won't get much of a chance to revise it any time soon and I wanted to post back what I was using in case someone else stumbled across this forum. It's welcome to copying/modifying/reusing/etc. Also, I welcome improvements seeing as I really didn't know what I was doing.

Usage: MaskAndSplit.sh input_file.jpg mask.gif
It will generate the pieces and place them in the current working directory naming them input_file.jpg-%d.png where %d is the piece number.
The mask and the input file need to be the same size...otherwise...well, I'm not sure what will happen!

Code: Select all

#!/bin/sh

# MaskAndSplit.sh
# 
#
# Created by Daniel Pfeiffer on 12/14/10.
# Copyright 2010 The Iona Group. All rights reserved.

# Based on multicrop by Fred Weinhaus (http://imagemagick.org/Usage/scripts/multi_crop)
# Original program Fred Weinhaus 1/30/2010,  revised 7/7/2010
#
# Modified by Anthony Thyssen, with different defaults and improved handling
# to ignore 'small images', and produce less verbose error reports.
#

#variables
grid=10		#grid size, in percent, of the original image
readin="/tmp/in_$$.mpc"
mask="/tmp/mask_$$.mpc"
active_mask="/tmp/active_mask_$$.mpc"

trap "rm -rf $readin $mask $active_mask; exit 0" 0

in_file="$1"
mask_file="$2"
base_name=`basename $in_file`

# read the in file
convert -quiet -regard-warnings "$in_file" +repage "$readin" ||  Error "File $in_file not readable as an image"


# get image width and height
width=`identify -ping -format "%w" $readin`
height=`identify -ping -format "%h" $readin`

# get grid spacing
wg=`convert xc: -format "%[fx:round($grid*$width/100)]" info:`
hg=`convert xc: -format "%[fx:round($grid*$height/100)]" info:`
num=`convert xc: -format "%[fx:round(100/$grid) - 2]" info:`

# load the mask
# convert any white space to red
convert $mask_file -fill red -opaque white $mask

# loop over grid and floodfill and trim to get individual mask for each image
k=0
y=0
for ((j=0;j<=$num;j++))
	do
 	x=0
 	y=$(($y + $hg))
	for ((i=0;i<=$num;i++))
		do
		x=$(($x + $wg))
		# test color to see if its not black
		testcolor=`convert $mask -channel rgba -alpha on -format "%[fx:u.p{$x,$y}=="none"?0:1]" info:`
		if [ $testcolor -eq 1 ]; then
			# Fill the first mask white AND remove the rest of the mask
			convert \( $mask -fill white -draw "color $x,$y floodfill" \) -fill black +opaque white $active_mask
			# Remove this section from the mask
			convert $mask -fill black -draw "color $x,$y floodfill" $mask
			
			# Apply the mask
			name="$base_name-$k.png"
			convert $readin $active_mask -compose multiply -composite -trim $name
			echo $name

			k=$(($k + 1))
		fi
	done
done

exit 0;

Re: Segmenting an image using a mask

Posted: 2010-12-14T18:46:37-07:00
by fmw42
Dan,

I tried to work on the multicrop script a bit today to see about modifying it. But under the current IM version it fails. see viewtopic.php?f=3&t=17678

You are using the same code line. So I would be curious what version of IM you are using. It might be helpful for the IM staff to know for comparison to see what has changed.

thanks

Fred

Re: Segmenting an image using a mask

Posted: 2010-12-14T18:50:08-07:00
by dpfeiffer
Hi Fred,

I'm on ImageMagick 6.6.4-9 2010-10-06 Q16.

--Dan

Re: Segmenting an image using a mask

Posted: 2010-12-14T18:50:56-07:00
by fmw42
dpfeiffer wrote:Hi Fred,

I'm on ImageMagick 6.6.4-9 2010-10-06 Q16.

--Dan

Thanks

Re: Segmenting an image using a mask

Posted: 2010-12-14T18:56:04-07:00
by dpfeiffer
Okay - so, in my ultimate use case for this, my images are (roughly) 20,000px by 5,000px (same with the mask). It will eventually be running on a dedicated xserve with plenty of power; for now, I'm testing on my MacBookPro.

I, of course, expect the processing of these images to take some time. I'm watching it run on my laptop, and the two lines that it really struggles on are:

Code: Select all

convert \( $mask -fill white -draw "color $x,$y floodfill" \) -fill black +opaque white $active_mask
and...

Code: Select all

convert $mask -fill black -draw "color $x,$y floodfill" $mask
Which is not what I would have expected since there's not much image data in the mask...it's just black and white and red.

Is there anyway to optimize these lines?

I realize it will run much faster on the xserve, but I still want it to be as optimized as possible.

Re: Segmenting an image using a mask

Posted: 2010-12-14T19:02:13-07:00
by fmw42
Anthony will be better at answering this. But I suspect that the -draw is slow (esp at floodfill)

Perhaps he will have some idea how to speed it up.

Re: Segmenting an image using a mask

Posted: 2010-12-14T19:05:32-07:00
by anthony
It is the segmentation that is the horible part.
If the masks were separate images, OR even just an array of values (rectangular areas), a faster, more direct methods can be used.

So the question is do you have that data?

Re: Segmenting an image using a mask

Posted: 2010-12-14T19:11:20-07:00
by dpfeiffer
Well, I have it in the form of my original mask. :-)

I'm sure I could figure out the coordinates from that.

I'm going to see how this performs on a much more powerful machine. I am sort of going for "ease of editing" to so that the locations of these cuts could be easily modified (hence the idea of using a mask). This process will generally run overnight and it won't have to process more than 5-10 files at a time. My goal is for it to take less than 20 minutes for one image.

Re: Segmenting an image using a mask

Posted: 2010-12-14T19:15:44-07:00
by dpfeiffer
Hm. What if instead of segmenting and applying the mask, it would iterate through the mask and determine the coordinates of the white spaces. Then it would use those coordinates to crop the original image. That sounds like it would be faster, but I'm not sure how to go about making that happen.

Re: Segmenting an image using a mask

Posted: 2010-12-14T19:19:08-07:00
by anthony
dpfeiffer wrote:Well, I have it in the form of my original mask. :-)
You could use multi-crop on your mask to get that data!

Code: Select all

multi_crop  mask.png maskjunk.png  > crop_geometry.txt
then edit that data to generate crop size and position, before using it like this...

Code: Select all

white read geometry; do
   convert image.png -crop $geometry +repage result_$geometry.png
done < crop_geometry.txt