Page 1 of 1

Extracting the nth adjacent sub-image ("undoing an +append")

Posted: 2015-12-12T06:59:13-07:00
by sas
Consider an input image that was created by horizontally appending three opaque rectangular images on a transparent background, for example:

Code: Select all

convert  \( logo: -resize 35% \)  rose:  granite:  -background Transparent -gravity North +append input.png
Image

The problem

Given an image like that, and an integer n, what's the best automated way to extract the nth original image and save it as a new file?

E.g. given the image shown above and the number 2, the result image would be:
Image (i.e. identical to the one that would be generated by "convert rose: result.png")

The sizes of the sub-images are not known up-front, but the following assumptions can be made:
  1. All sub-images are rectangular and fully opaque.
  2. All sub-images have different heights, and are aligned to the top of the input image.
  3. All pixels which are not part of a sub-image are fully transparent.
My approach so far

So far, my best idea for solving this is to use "-scale" to vertically average the image to a single pixel row, and then take the alpha channel of that and output it as a raw text stream:

Code: Select all

convert input.png -scale 'x1!' -alpha extract txt:-
This outputs one line per pixel in our row, looking like this:

Code: Select all

...
4,0: (17944.1,17944.1,17944.1)  #464646  gray(70)
5,0: (17944.1,17944.1,17944.1)  #464646  gray(70)
6,0: (17944.1,17944.1,17944.1)  #464646  gray(70)
...
Then I could use an Awk/Perl/shell script to filter out the lines which have a different color value than their preceding line... The first numbers of those filtered lines would correspond to the x coordinates of the sub-images.

If x is the nth coordinate and w is the difference between the (n+1)th and nth coordinate, I could then extract the wanted sub-image like this:

Code: Select all

convert input.png  -crop "$w"x0+"$x"+0  -bordercolor Transparent -border 1x1 -trim  +repage  result.png
(The "-crop" operation would extract the correct column, and "-bordercolor Transparent -border 1x1 -trim" would remove the transparent area below the sub-image.)

Question

Would this be a good way to do it? Is there a better (faster/simpler/safer) approach?

In particular, is it possible to do everything in a single 'convert' call, without a shell script?
(I'm using ImageMagick 6.9.2-6 Q16 x86_64 2015-11-23)

Re: Extracting the nth adjacent sub-image ("undoing an +append")

Posted: 2015-12-12T11:41:28-07:00
by fmw42
That is a pretty good method. But it will have trouble if you have two neighboring images that are both the same height even though they have different color content, since you are just checking the average opacity in each column in your appended image.

If there was even just one transparent spacer column between images, this task would be much easier, via my multicrop script.

If you can assign a different transparency to each image, then you could extract the alpha channel and use -connected-components to extract the bounding boxes of each section. See http://www.imagemagick.org/script/conne ... onents.php

This is doable by scaling back up your averaged 1 row to the same height as the appended image. Then multiply that by the original alpha channel. Then process this image with CCL. So in unix syntax (sorry I do not know how to do this in Windows)

Code: Select all

dim=`convert ijeNZWb.png -format "%wx%h" info:`
data=`convert ijeNZWb.png \
\( -clone 0 -alpha extract \) \( -clone 1 -scale x1! -scale $dim! \) \
-delete 0 -compose multiply -composite -write tmp1.gif \
-define connected-components:verbose=true -connected-components 4 null:`
echo "$data"
bboxArr=(`echo "$data" | tail -n +2 |\
sed 's/^[ ]*//' | cut -d\  -f2`)
colorArr=(`echo "$data" | tail -n +2 |\
sed 's/^[ ]*//' | cut -d\  -f5`)
num=${#bboxArr[*]}
echo $num
4

echo ${bboxArr[*]}
224x168+0+0 128x128+294+0 198x122+224+46 70x46+224+0

echo ${colorArr[*]}
gray(255) gray(194) gray(0) gray(70)
Now just take the list in bboxArr and colorArr and loop over each one and crop the appended images at those crop coordinates ignoring the one or more that are black=gray(0). I have written out the tmp1.gif image that is the product alpha channel image that will be used for CCL, so you can correlate what is happening.

Code: Select all

name=`convert ijeNZWb.png -format "%t" info:`
for ((i=0; i<num; i++)); do
color="${colorArr[$i]}"
if [ "$color" != "gray(0)" ]; then
convert ijeNZWb.png -crop "${bboxArr[$i]}" +repage -alpha off ijeNZWb_$i.png
fi
done

Note the full information returned from CCL for you image before my filter for just the bounding boxes and color was:

Code: Select all

Objects (id: bounding-box centroid area mean-color):
  0: 224x168+0+0 111.5,83.5 37632 gray(255)
  2: 128x128+294+0 357.5,63.5 16384 gray(194)
  3: 198x122+224+46 295.6,121.9 13660 gray(0)
  1: 70x46+224+0 258.5,22.5 3220 gray(70)
The gray values represent the average alpha channel values from your 1D scaled down image and the transparent black background regions

However, this method also fails if two neighboring images are the same heights.

Re: Extracting the nth adjacent sub-image ("undoing an +append")

Posted: 2015-12-13T00:58:20-07:00
by sas
Thanks for the reply fmw42!

What would you consider the advantages of this method?
(From what I can see, the text output is easier for the shell script to parse than with my method, but in return it needs one extra convert call - to get the original image dimensions. Would it be possible to avoid that separate call?)

Also, it's not really clear to me from the documentation what "-connected-components" actually does to the image.
It says "detected objects are uniquely labeled", but it doesn't define anywhere what "labeled" means in this context.
I know in this example you discard whatever it does and just use it for its "verbose=true" side-effect, but I'm trying to understand it better.

If it were possible to refer to the nth labeled region inside same command that did the "-connected-components", this could remove the need for the shell-script altogether, no?

Re: Extracting the nth adjacent sub-image ("undoing an +append")

Posted: 2015-12-13T11:42:23-07:00
by fmw42
Just a different way to do it.

The extra call to get the width and height is needed to know how to expand the image back to its original size. This cannot be avoided in IM 6, but could in IM 7

The labels are the id #s and are put in the output image as pixel values if one gives an output rather than null:

No way in -connected-components to access and output just one id's region. That is why I had to script the list.

Re: Extracting the nth adjacent sub-image ("undoing an +append")

Posted: 2015-12-13T12:52:59-07:00
by sas
Understood, thanks.

PS: I didn't realize that IM 7 is already in beta. Nice!

Re: Extracting the nth adjacent sub-image ("undoing an +append")

Posted: 2015-12-16T06:46:39-07:00
by sas
fmw42, I ended up using your approach, and it is working well.

But I have a follow-up question: Why do you do this...

Code: Select all

\( -clone 0 -alpha extract \) \( -clone 1 -scale x1! -scale $dim! \) -delete 0
instead of just this?

Code: Select all

-alpha extract \( -clone 0 -scale x1! -scale $dim! \)
It seems to work just as well when I try it.

Is there some subtle difference I should be wary of?

Re: Extracting the nth adjacent sub-image ("undoing an +append")

Posted: 2015-12-16T10:23:13-07:00
by fmw42
I guess I was not thinking efficiently at the time. Your command should work fine.