Extract sprites/images from a bigger image with transparent background

Questions and postings pertaining to the usage of ImageMagick regardless of the interface. This includes the command-line utilities, as well as the C and C++ APIs. Usage questions are like "How do I use ImageMagick to create drop shadows?".
Post Reply
User avatar
fieel
Posts: 6
Joined: 2018-02-07T10:25:57-07:00
Authentication code: 1152
Location: Switzerland

Extract sprites/images from a bigger image with transparent background

Post by fieel »

Hello,

for a project i have to take a physical deck of playing cards and turn it into a digital collection of pictures.

Basically, i have to scan a black (or any solid color) piece of cardboard with an entire deck of playing cards on it. Then, after scanning and once i have the high-quality file, i have to extract the single cards into single files. I have to automatize this process so i thought ImageMagick might be the perfect tool for the job.

I never used ImageMagick, i just want to find out if it's the right tool so i can start using and learning how to use it.

Initially, i tried using shoebox which has a "sprite extract" feature which is perfect in this case, except for one thing: it works only from the GUI... the CLI commands are broken. Here are some screenshots of the resulting behavior that i want to re-create:

Transparent background image with collection of cards:
Image

Result:
Image
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Extract sprites/images from a bigger image with transparent background

Post by fmw42 »

What platform are you on? Can you provide your input image without arrows and annotation?
User avatar
fieel
Posts: 6
Joined: 2018-02-07T10:25:57-07:00
Authentication code: 1152
Location: Switzerland

Re: Extract sprites/images from a bigger image with transparent background

Post by fieel »

fmw42 wrote: 2018-02-07T12:08:21-07:00 What platform are you on? Can you provide your input image without arrows and annotation?
Sure thing! Here it is: https://i.imgur.com/kh6MRaf.png
Keep in mind this is just a picture i'm using for testing, the end result will be a big high-quality picture.
I'm working on windows btw.
Bonzo
Posts: 2971
Joined: 2006-05-20T08:08:19-07:00
Location: Cambridge, England

Re: Extract sprites/images from a bigger image with transparent background

Post by Bonzo »

And what IM version as that can have an effect?
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Extract sprites/images from a bigger image with transparent background

Post by fmw42 »

I do not code for windows, but here is how I would do that for Unix.

Add 1 pixel transparent border and make sure all color under the transparency is white:

Code: Select all

convert cards.png -bordercolor none -border 1 -background white -alpha background -bordercolor white cards_border.png
Image

Threshold to black and white:

Code: Select all

convert  cards_border.png -alpha off -negate -threshold 0 -type bilevel cards_white_t0.png
Image

Run connected components labeling to extract the bounds of the white thresholded cards and crop the white background image to extract the cards.

Code: Select all

OLD_IFS=$IFS
IFS=$'\n'
arr=(`convert cards_white_t0.png \
-define connected-components:verbose=true \
-define connected-components:area-threshold=10 \
-define connected-components:mean-color=true \
-connected-components 8 \
null: | tail -n +2 | sed 's/^[ ]*//'`)
IFS=$OLD_IFS
num=${#arr[*]}
for ((i=0; i<num; i++)); do
bbox=`echo "${arr[$i]}" | cut -d\  -f2`
color=`echo "${arr[$i]}" | cut -d\  -f5`
if [ "$color" = "gray(255)" ]; then
convert cards_border.png -crop $bbox +repage -background none -deskew 40% cards_$i.png
fi
done
Results in a zip archive:
http://www.fmwconcepts.com/misc_tests/cards/Archive.zip
Bonzo
Posts: 2971
Joined: 2006-05-20T08:08:19-07:00
Location: Cambridge, England

Re: Extract sprites/images from a bigger image with transparent background

Post by Bonzo »

Could you also add a -deskew and -trim to help clean up the images in the process fmw42?
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Extract sprites/images from a bigger image with transparent background

Post by fmw42 »

I have modified the code I posted earlier to keep the background transparent about the image and the output card images. I also added a deskew to try to straighten the resulting card images, but it does not always help much. Some results are corrected better than others.
User avatar
fieel
Posts: 6
Joined: 2018-02-07T10:25:57-07:00
Authentication code: 1152
Location: Switzerland

Re: Extract sprites/images from a bigger image with transparent background

Post by fieel »

As far as i noticed it's working perfectly on Linux and it's what i was looking for: thank you!

I'll have a better look as soon as i have more time then i'll have to convert it into a Windows script and post back here.

Another question: what's the best way to automatically deal with a similar image without having already a transparent background? Fortunately, even if not perfect, the background will be some kind of solid color, like this:
Image
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Extract sprites/images from a bigger image with transparent background

Post by fmw42 »

The best way is to be sure that the background color is completely unique relative to any other color on the cards. Also not to have JPG for the input. JPG is lossy and any solid color will not be truly constant. That way you can use something like

Code: Select all

convert image.png -transparent your_background_color new_image.png
Otherwise, one can do the following, but one has to use an area-threshold that is larger than any black in the cards but smaller than the size of the cards.

Input:
Image

Code: Select all

convert cards2.png -bordercolor black -border 1 cards2_border.png
Image

Code: Select all

convert cards_border2.png -auto-level -fuzz 20% -fill black -opaque black -fill white +opaque -type bilevel black mask.png
Image

Code: Select all

OLD_IFS=$IFS
IFS=$'\n'
arr=(`convert mask.png \
-define connected-components:verbose=true \
-define connected-components:area-threshold=9000 \
-define connected-components:mean-color=true \
-connected-components 8 \
new_mask.png | tail -n +2 | sed 's/^[ ]*//'`)
IFS=$OLD_IFS
num=${#arr[*]}
for ((i=0; i<num; i++)); do
bbox=`echo "${arr[$i]}" | cut -d\  -f2`
color=`echo "${arr[$i]}" | cut -d\  -f5`
if [ "$color" = "gray(255)" ]; then
convert \( cards2_border.png new_mask.png -alpha off -compose copy_opacity -composite \)  \
-crop $bbox +repage -background none -deskew 40% cards_$i.png
fi
done
new_mask.png:
Image


zip archive of individual cards:
http://www.fmwconcepts.com/misc_tests/c ... rchive.zip
User avatar
fieel
Posts: 6
Joined: 2018-02-07T10:25:57-07:00
Authentication code: 1152
Location: Switzerland

Re: Extract sprites/images from a bigger image with transparent background

Post by fieel »

I'm trying to implement the script but i still have one problem.

Given that the initial image i'll be working on will be a scan done by a scanner or even a photograph, the background color won't be truly unique.

It will probably be some kind of shade of a solid color (which i still have to choose, not a color used by the cards themselves for sure though).

What's the best way to filter and delete the background so i can have a transparent background if i don't have a solid color?
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

Re: Extract sprites/images from a bigger image with transparent background

Post by snibgo »

Probably something like:

Code: Select all

convert in.png -fuzz 10% -transparent #abc out.png
... where abc is the average background colour and the fuzz percentage covers the range of background colours.
snibgo's IM pages: im.snibgo.com
User avatar
fieel
Posts: 6
Joined: 2018-02-07T10:25:57-07:00
Authentication code: 1152
Location: Switzerland

Re: Extract sprites/images from a bigger image with transparent background

Post by fieel »

Okay i took all the data from this topic, some info i found on the internet, some personal thoughts and tried to create a proper script. It's not that complicated as of now.

Kinda simplified but these are the steps:

1. Detect any background and remove it, replacing it with transparency. This is accomplished by this awesome prebuilt script from hackerb9 https://github.com/hackerb9/mktrans
2.1 Add a 1 px white border around cards
2.2 Create a mask using the 1px border to understand where cards are
3. Use the mask to extract the single cards

Now i'd like to apply some minimal antialiasing before extracting the single cards but only after creating the mask, this is very important because i need a clear contrasting line between white and black when extracting the cards using the mask otherwise it won't work properly.

This is some relevant code from the script:

Code: Select all


	#Variables declaration
	FILENAME=$1
	FILENAME_NO_EXTENSION=$(echo $FILENAME | cut -d '.' -f 1)
	DECKNAME=$2

	echo ''
	echo '*******************************'
	echo '  Input file: '$FILENAME
	echo '  Deck name:  '$DECKNAME
	echo '*******************************'
	echo ''

	#Replace bg color with transparent alpha channel
	echo '1. Removing background...'
	bash ./mktrans -A $FILENAME

	#Add 1 pixel transparent border and make sure all color under the transparency is white:
	convert $FILENAME_NO_EXTENSION-transparent.png -bordercolor none -border 1 -background white -alpha background -bordercolor white tmp_border.png

	#Threshold to black and white - creating a mask used to cut single cards in the next step
	echo '2. Creating mask...'
	convert tmp_border.png -alpha off -negate -threshold 0 -type bilevel tmp_mask.png

	#Run connected components labeling to extract the bounds of the white thresholded cards using the mask
	#and crop the white background image to extract the cards
	echo '3. Extracting cards...'
	OLD_IFS=$IFS
	IFS=$'\n'
	arr=(`convert tmp_mask.png \
	-define connected-components:verbose=true \
	-define connected-components:area-threshold=10 \
	-define connected-components:mean-color=true \
	-connected-components 8 \
	null: | tail -n +2 | sed 's/^[ ]*//'`)
	IFS=$OLD_IFS
	num=${#arr[*]}
	for ((i=0; i<num; i++))
	do
		bbox=`echo "${arr[$i]}" | cut -d\  -f2`
		color=`echo "${arr[$i]}" | cut -d\  -f5`
		if [ "$color" = "gray(255)" ]; then
			convert tmp_border.png -crop $bbox +repage -background none -deskew 40% ${DECKNAME}${i}.png
			echo -n '.'
		fi
	done
User avatar
fieel
Posts: 6
Joined: 2018-02-07T10:25:57-07:00
Authentication code: 1152
Location: Switzerland

Re: Extract sprites/images from a bigger image with transparent background

Post by fieel »

Any help? I'm having an hard time finding the correct and best way to approach this problem.

I think the only solution is applying some antialiasing to every single generated card as output, but i'm sure there must be another (and less CPU intensive) way.

If i soften edges before extracting the single cards the extraction won't work properly due to the absence of clear and linear boundaries between white and black in the mask, this is the biggest problem here.
Post Reply