How can I draw mirrored text onto an image using ImageMagick? - ruby

I'm using ImageMagick (through the MiniMagick Ruby Gem) to crop an image, I also add an annotation to the top of it, but I want it to be mirrored.
I know ImageMagick has a 'flop' command which mirrors an image, but I'm not sure how to generate the text, flop it and compose it onto my image.
Here is how I currently draw the text (non mirrored). This code uses MiniMagick, but it translates pretty directly to actual ImageMagick commands.
image.combine_options do |c|
c.resize "1000"
c.font Rails.root.join('app/assets/fonts/Menlo.ttc').to_s
c.fill '#888888'
c.pointsize '16'
c.draw "text 0,0 '#{annotation}'"
end
How can I modify or replace this to draw mirrored text?

First, you should, for your Minimagick code, do yourself the 'translate pretty directly to actual ImageMagick commands', if you really are interested to get the biggest possible input. Not everyone here wants to learn Minimagick first, before he can chime in with his suggestions.
Second, you didn't tell which way you want your text mirrored: Left-right? Upside-down? Combined?
Third, try to replace these 2 lines
c.pointsize '16'
c.draw "text 0,0 '#{annotation}'"
by these 3 lines
c.pointsize '16'
c.draw "text 0,0 '#{annotation}'"
c.flop
or by these 3 lines
c.pointsize '16'
c.draw "text 0,0 '#{annotation}'"
c.flip
or by these 4 lines
c.pointsize '16'
c.draw "text 0,0 '#{annotation}'"
c.flip
c.flop

Now that Nathan admits he's 'familiar' with -flip and -flop for images only, let's show him how one can use that to nevertheless get flipped and flopped text annotations onto unchanged images.
See these two examples:
Left with with normal text, right with mirrored text (left-right mirrored - Nathan didn't respond to the question if he wanted this, or if he wanted top-bottom mirroring).
How was it done?
The left image's annotation was done with the following command:
convert \
logo: \
-fill white \
-undercolor '#00000080' \
\( \
-gravity west \
-pointsize 32 \
-annotate +0+40 \
" Dude! Listen, don't argue... " \
\) \
-scale 310x \
normal.png
So this result is the basis for the next step. In the next step we will modify this command to create a mirrored text annotation. But, since we cannot write mirrored text directly, we use one of the many tricks up in our sleeves:
Mirror the image first (first -flop).
Write normal text on mirrored image.
Mirror the resulting image one more time (second -flop). This way...
...main image is back to normal,
...annotation text is mirrored now.
And this leads to the left image's annotation command:
convert \
logo: \
-flop \
-fill white \
-undercolor '#00000080' \
\( \
-gravity east \
-pointsize 32 \
-annotate +0+40 \
" Dude! Listen, don't argue... " \
\) \
-flop \
-scale 310x \
mirrored.png
Easy, eh?
(Just notice how we also had to change the gravity from west to east so that the text appears on the same spot...)
It's Nathan's own job now to translate this algorithm into Minimagick's language.

Related

Upload and crop image and add text from frontend in wordpress

My client needs for his visitors to be able to upload, zoom and crop an image, add text over the cropped image and choose a position of the text.
Something like example on this image: http://devadesign.biz/example.jpg
After clicking on submit button the cropped image, with text on it, has to be saved in a specific folder.
Is there any plugin or tutorial for something like that?
I've already tried some plugins and scripts like those:
https://artisansweb.net/upload-crop-resize-image-using-jquery-php/
https://www.sanwebe.com/2012/05/ajax-image-upload-and-resize-with-jquery-and-php
https://www.sitepoint.com/crop-and-resize-images-with-imagemagick/
http://www.croppic.net/
https://foliotek.github.io/Croppie/
...but none of them supports adding text.
Thank you!
You can do that in Imagemagick. Here is an example:
Input:
line 1: read input and put into memory and delete input
line 2: add contast to memory input
line 3-5: add white to memory input
line 6-7: create a white image with a black rectangle in the middle as a mask
line 8: composite the two modified input images using the mask
line 9: set up the fill and stroke for creating a black outline around the middle
line 10: draw the black polygon rectangle
line 11: set up the pointsize and colors for the text
line 12: draw the text with annotate
line 13: save the output
convert lena.jpg -write mpr:img +delete \
\( mpr:img -level 10x100% \) \
\( mpr:img -alpha set \
-channel a -evaluate set 80% +channel \
-background white -flatten \) \
\( -clone 0 -fill white -colorize 100 \
-fill black -draw "rectangle 64,64 192,192" -alpha off \) \
-compose over -composite \
-fill none -stroke black \
-draw "polygon 64,64 192,64 192,192 64,192" -alpha off \
-gravity center -pointsize 24 -fill white -stroke white \
-annotate +0+0 "TESTING" \
result.png
For drawing options see https://imagemagick.org/Usage/draw
Wordpress has an Imagemagick plugin. See https://wordpress.org/plugins/tags/imagemagick/

How to extract photos from JPG with white background?

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

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:

Split a Bash script with ImageMagick instructions into smaller pieces?

I have this bash script, feeding drawing information into ImageMagick, which starts like this:
#!/bin/bash
convert -size 2200x2200 xc:white \
-fill '#FFFEFF' -draw 'point 1112,1111' \
-fill '#FFFEFE' -draw 'point 1112,1112' \
-fill '#FFFEFE' -draw 'point 1111,1112' \
-fill '#FFFEFE' -draw 'point 1110,1112' \
-fill '#FFFEFE' -draw 'point 1110,1111' \
******ON & ON 4.2 MILLION LINES MORE*******
spectrumspiral.png;
My problem is I keep getting warnings about argument list too long, terminal says 'Killed', warning about 'fork: cannot allocate memory etc.
I've tried adjusting ulimit -s to a much higher value to no avail. Really want to make this image. Any idea how I can feed the terminal chunks of this script at a time? Or something to that end.
I've heard xargs can be used for things like this, but I haven't been able to find a specific implementation that fits the nature of this problem.
If you create an image with 4 pixels in it (1 red, 1 white, 1 blue and 1 black), you can tell ImageMagick to output the resulting image as text file as follows:
convert -size 1x1 xc:red xc:white xc:blue xc:black +append -depth 8 -colorspace RGB image.txt
# ImageMagick pixel enumeration: 4,1,255,rgb
0,0: (255,0,0) #FF0000 rgb(255,0,0)
1,0: (255,255,255) #FFFFFF rgb(255,255,255)
2,0: (0,0,255) #0000FF rgb(0,0,255)
3,0: (0,0,0) #000000 rgb(0,0,0)
By the same token, if you feed that text file into ImageMagick, it can recreate the image:
cat image.txt | convert txt:- output.img
So, all we need to do is convert your -fill commands into a text file of the format that ImageMagick likes. So we can do this to yourFile
sed -E 's/.*(#[0-9A-F]+).* ([0-9]+)\,([0-9]+).*/\2,\3:\1/g' yourFile
and we will get something like this:
0,0:#3C4AD8
0,1:#269531
0,2:#CF2C7C
...
...
which we can then pipe into awk to rearrange how ImageMagick likes it:
awk -F: '
BEGIN{print "# ImageMagick pixel enumeration: 2200,2200,255,rgb"}
{
coords=$1;colour=$2
rh="0x" substr(colour,2,2);
gh="0x" substr(colour,4,2);
bh="0x" substr(colour,6,2);
r=strtonum(rh);
g=strtonum(gh);
b=strtonum(bh);
printf "%s: (%d,%d,%d) %s\n",coords,r,g,b,colour;
}'
So, if we put all that together, the following script should be able to create your beloved image into the file spectrumspiral.png:
#!/bin/bash
sed -E 's/.*(#[0-9A-F]+).* ([0-9]+)\,([0-9]+).*/\2,\3:\1/g' yourFile | awk -F: '
BEGIN{print "# ImageMagick pixel enumeration: 2200,2200,255,rgb"}
{
coords=$1;colour=$2
rh="0x" substr(colour,2,2);
gh="0x" substr(colour,4,2);
bh="0x" substr(colour,6,2);
r=strtonum(rh);
g=strtonum(gh);
b=strtonum(bh);
printf "%s: (%d,%d,%d) %s\n",coords,r,g,b,colour;
}' | convert txt:- spectrumspiral.png
Try using the shell's here-document
#!/bin/bash
convert - <<EOS spectrumspiral.png
-size 2200x2200 xc:white
-fill '#FFFEFF' -draw point '1112,1111'
-fill '#FFFEFE' -draw 'point 1112,1112'
-fill '#FFFEFE' -draw 'point 1111,1112'
-fill '#FFFEFE' -draw 'point 1110,1112'
-fill '#FFFEFE' -draw 'point 1110,1111'
# ******ON & ON 4.2 MILLION LINES MORE*****
EOS
My system doesn't have convert so I can test that this works.
Most linux programs (including convert) can accept input from StdIn, which in the cmd-line above is represented by the - char. The - tells the command to expect input, not from file, but as if it was being typed at the keyboard, ie StdIn. The <<EOS .... EOS is the here-doc, and it represents bash 'typing' in all of that text into the commands stdIn input.
You may or may not need all of the quoting, if you already have there, I don't think it will hurt. If this doesn't work as is, use a small sample file of input (like above) and test various scenarios until it creates a file for you.
IHTH

make all values but one in large raster file 0

I have a large raster file downloaded from Earth Engine. I want to turn it to a boolean file, keeping only one value (13) and make all other values either NA or 0. The file is so large it crashes QGIS and ArcMap when I try to process it, is there a way to do this using GDAL or bash? The file is a tif file.
You can do that with ImageMagick which is installed on most Linux distros and is available for macOS and Windows.
Make a test image at the command line - value 13 in the middle, red and blue around that:
convert -size 30x20 xc:"gray(13)" \
-bordercolor red -border 10 \
-bordercolor blue -border 10 start.tif
Now fill with black, everything that is not value 13:
convert start.tif -fill black +opaque "gray(13)" result.tif
Or, somewhat easier to see - fill everything that is not value 13 with cyan and change everything that is value 13 to yellow:
convert start.tif \
-fill cyan +opaque "gray(13)" \
-fill yellow -opaque "gray(13)" result.tif
Here's a gdal solution:
Your input is input.tif:
gdal_calc.py --calc="A==13" -A input.tif --type=Byte --outfile=output.tif
With R you can do
library(raster)
library(rgdal)
r <- raster("input.tif")
x <- calc(r, function(i){ i==13 }, filename="output.tif", datatype="INT1U")
Or use raster::reclassify

Resources