How to extract photos from JPG with white background? - image

I have a JPG file which contains multiple photos over a white background.
I'm looking for a CLI tool which can extract photos from the source JPG (without supplying coordinates) into separate JPG files maintaining quality and photo resolution.
From some research I suspect ImageMagick can achieve this though unsure of the correct CLI command.
If useful, I'm on OSX 10.13.2 and have ImageMagick 7.0.7-28 installed.

Here are two ways to do this in Unix using Imagemagick. I just cropped out your base image from your diagram, since I was not sure it that was part of your image or not. If it is part of the image, then you would have to trim that off first using -trim.
Input:
The first is my script, multicrop2:
(-f 10 is the fuzz factor for extracting the background)
(-u 3 means no attempt to unrotated the results)
multicrop2 -f 10 -u 3 image.jpg resulta.jpg
Processing Image 0
Initial Crop Box: 113x84+81+89
Processing Image 1
Initial Crop Box: 113x67+144+10
Processing Image 2
Initial Crop Box: 113x66+10+11
The second is using Imagemagick -connected-componets (which is what I use in my script)
What this does is:
1) fuzzy flood fill the background to transparent (since jpg is loss and does not preserve a uniform background.
2) change the color under the transparent to white and remove the transparency
3) change anything not white to black
4) apply -connected-components to throw out areas smaller than 400 pixel area and extract each bounding box and color
5) if the color is gray(0), i.e. black, then crop the original image to the bounding box and save to disk
OLDIFS=$IFS
IFS=$'\n'
arr=(`convert image.jpg -fuzz 10% -fill none -draw "matte 0,0 floodfill" \
-background white -alpha background -alpha off \
-fill black +opaque white -type bilevel \
-define connected-components:verbose=true \
-define connected-components:mean-color=true \
-define connected-components:area-threshold=400 \
-connected-components 4 null: | tail -n +2 | sed 's/^[ ]*//'`)
IFS=$OLDIFS
num=${#arr[*]}
j=0
for ((i=0; i<num; i++)); do
bbox=`echo "${arr[$i]}" | cut -d\ -f2`
color=`echo "${arr[$i]}" | cut -d\ -f5`
if [ "$color" = "gray(0)" ]; then
convert image.jpg -crop $bbox +repage resultb_$j.jpg
j=$((j+1))
fi
done
EDIT: Add processing of your actual image
Input:
The first thing to note is that your actual two images are right at the right side, but there is a black edge there. Also one at the top. That black edge connects the two image so that they cannot easily be separated by the multicrop2 script. So you need to shave off the right side by enough pixels to remove that edge. There is also the edge at the top and you can shave that off if you want. If you do, you can reduce the -d argument. The -d argument needs to be smaller that the area of the smallest image you want to extract and larger than any other minor noise or the stripe at the top in area. So I clip off 20 pixels from the right side and then use multicrop2 with a very large value for -d. I have chosen a value for -f of 8, which is seems to be in a rather narrow range due to the non-constant background. You can add -m save to look at the mask the script creates to see that you get a good separation between your two images. I seed the processing at -c 20,20 to avoid the black border at the top of your image, so that the script gets a good measure of the background color for the flood fill step.
convert test.jpeg -gravity east -chop 20x0 tmp.png
multicrop2 -c 20,20 -f 8 -d 100000 tmp.png result.jpg
Processing Image 0
Initial Crop Box: 2319x1627+968+2153
Processing Image 1
Initial Crop Box: 2293x1611+994+436

Related

Find an especific color coordenates on an image

I would like to find the coordinates of the first appearance of a certain color, for example green, on an image so can be used in a bash script.
I've been trying to use Imagemagick but can't find a way to solve the problem.
Can this be done with Imagemagick or should I use anything else?
Here is one way in ImageMagick using sparse-color: (provided the image is fully opaque). Sparse-color will read out the x,y coordinates and color of all fully opaque pixels. Then you can use unix tools to get the first one.
Create red and blue image:
convert -size 10x10 xc:red xc:blue -append x.png
Find first rgb(0,0,255) i.e. blue colored pixel
convert x.png sparse-color: | tr " " "\n" | grep "srgb(0,0,255)" | head -n 1
Result
0,10,srgb(0,0,255)
Similar results can be achieved using txt: in place of sparse-color. But the unix commands for filtering would be a bit different.

Count how many pixels with a specific color in an image?

I want to loop through a folder of images and output to console how many pixels are #333212, how many are #332211 etc. Is this possible in PHP? I found a package that manipulates images but not one that can detect colors of each pixel. Does such a tool or function exist in the PHP library?
EDIT: Doesn't have to be in PHP, the less packages I have to install the better.
You can do this quite easily with ImageMagick, like this. Say we want to count the red pixels...
# First create a little test strip with black, white, red, green and blue parts
convert -size 50x50 xc:black xc:white xc:red xc:lime xc:blue +append out.png
Now convert anything non-red to black so that only red pixels remain
convert out.png -fill black +opaque red n.png
Now count the red pixels by cloning the resulting picture and making the clone fully black (by setting everything to zero), and running a comparison to count how many pixels are not black
convert n.png \
\( +clone -evaluate set 0 \) \
-metric AE -compare \
-format "%[distortion]" info:
2500
And 2500 looks like 50px by 50px to me :-)
Note
The AE is the Absolute Error, i.e. a simple count of the number of differing pixels. The -format "%[distortion]" info: part causes ImageMagick to output the count (%distortion) as a number (info:) rather than as an image.
Obviously, you replace red with "#333212" for your problem.
You can also do all that in one visit, like this:
convert input.png \
-fill black +opaque red \
\( +clone -evaluate set 0 \) \
-metric AE -compare \
-format "%[distortion]" info:

How do I fill certain percentage by height of an image using ImageMagick commands?

I have an image with around 20% of its bottom filled with white color. But the image has some dots of other colors than white. I have 100s of such images which I would like to remove those dots from.
I have been using ImageMagick commands, and bash scripts to automate several tasks but I cannot find any command to fill certain percentage of an image from bottom by a solid color.
The dots are marked by arrow in the screenshot. A sample command or a hint would be great!
I achieved the goal by calculating the height, taking percentage (approximate) of the image's height and filling a white rectangle.
# A tool to fill up 10% of the bottom of given image
# by white color. Useful to remove unnecessary colors
# at the bottom of image.
# Usage: this_script.sh required_image.jpg
#!/bin/bash
image=$1
right=`identify -format %w $image`;
bottom=`identify -format %h $image`;
top=`expr $bottom \* 9 / 10 | bc`;
left=0
convert $image -fill white -draw "rectangle ${left},${top},${right},${bottom}" $image
This can be automated for several images in a folder like:
for img in *.jpg; do bash <script>.sh $img; done
You can do this in a single step like this:
convert in.png \
\( +clone -gravity south -crop x10% -evaluate set 100% \) \
-composite out.png
Essentially, we read in the image and then do some "aside processing" - a delightful term coined by ImageMagick guru Kurt Pfeifle. The "aside processing" starts with (\ and ends with \). In there, we clone the image (i.e. create a copy of it) and chop off the bottom 10%, which we then fill with white. At the end of the "aside processing", this white image is still in our image stack so we tell ImageMagick to composite that on top of the original.
Result:

Detecting mostly empty images using imagemagick

I'd like to use imagemagick or graphicsmagick to detect whether an image has basically no content.
Here is an example:
https://s3-us-west-2.amazonaws.com/idelog/token_page_images/120c6af0-73eb-11e4-9483-4d4827589112_embed.png
I've scoured Fred's imagemagick scripts, but I can't figure out if there is a way to do this:
http://www.fmwconcepts.com/imagemagick/
Easiest way would be to use -edge detection followed by histogram: & text:. This will generate a large list of pixel information that can be passed to another process for evaluation.
convert 120c6af0-73eb-11e4-9483-4d4827589112_embed.png \
-edge 1 histogram:text:- | cut -d ' ' -f 4 | sort | uniq -c
The above example will generate a nice report of:
50999 #000000
201 #FFFFFF
As the count of white pixels is less then 1% of black pixels, I can say the image is empty.
This can probably be simplified by passing -fx information to awk utility.
convert 120c6af0-73eb-11e4-9483-4d4827589112_embed.png \
-format '%[mean] %[max]' info:- | awk '{print $1/$2}'
#=> 0.00684814
If you are talking about the amount of opaque pixels vs the amount of transparent pixels, then the following will tell you the percentage of opaque pixels.
convert test.png -alpha extract -format "%[fx:100*mean]\n" info:
39.0626
Or if you want the percentage of transparent pixels, use
convert test.png -alpha extract -format "%[fx:100*(1-mean)]\n" info:
60.9374

ImageMagick and spritesheet

I'm working on a little script that generates a sprite sheet. I have 6 spritesheets and I need to re-organize them and put their content (once ordered) in a unique file.
I logically chose to use ImageMagick. But here I'm stuck.
Here is what I have so far :
convert '%d.png[0-5]' \( -crop 456x912+0+0 -crop 3x6+0+0# +append \) -append test.png
This command line takes my 6 files (0.png to 5.png) crop them, and split them into 18 sprites. Once splited, the 18 sprites are aligned horizontally and then aligned vertically with the 18 previous one.
The problem is this command seems to only aligned them horizontally. Instead of being composed of 18x6 sprites, test.png is composed by 108x1 sprites.
Any idea how to perform this in one command ?
You might try another imagemagick tool, "montage," for this one.
$ for i in `seq 1 18 `; do convert -background none -fill black -size 32x32 -pointsize 14 caption:"$i" $i.png; done
$ montage `ls ?.png` `ls ??.png` -tile 6x3 -geometry 32x32 tile.jpg

Resources