I have an image:
and I want to crop this image and extract sub images by horizontal black(gray) line and got smth like this (list of sub images):
1.
2.
and so on...
How can I do it with ImageMagick? Thanks for help.
Here is one way to do that in ImageMagick in Unix syntax and bash scripting. Threshold the image.
Add a black border all around. Then use connected components processing to find all the large white rectangles and their bounding boxes. Put those in an array. Then sort the array by Y value (top of bounding boxes). Then loop over each bounding box and crop the input image.
Input:
bboxArr=(`convert math.png -threshold 75% -bordercolor black -border 1 -type bilevel \
-define connected-components:exclude-header=true \
-define connected-components:area-threshold=100 \
-define connected-components:mean-color=true \
-define connected-components:verbose=true \
-connected-components 8 null: | grep "gray(255)" | awk '{print $2}'`)
num="${#bboxArr[*]}"
sortedArr=(`echo ${bboxArr[*]} | tr " " "\n" | sort -n -t "+" -n -k3,3 | tr "\n" " "`)
for ((i=0; i<num; i++)); do
cropval=${sortedArr[$i]}
convert math.png -crop $cropval +repage math_$i.png
done
Output (showing first 4 out of 11):
ImageMagick automatically sorts them by largest area first. So I had to sort them by Y.
Related
Take this image of a circle and a square on a transparent background (png).
I want to split this image by region. The regions are defined by connected color. I have a circle region. And a square region.
The output would be, one image file with the circle on it
and a second image file with the square on it.
Can ImageMagick do this? Can another tool do this? How can I do this?
Here is how to do that in ImageMagick. I note that your posted image has a white background, not a transparent one. However, I have changed the white to transparent. I first get the bounding boxes of the objects using connected components. Then I loop over each bounding box and crop it out and put it back into a transparent image using the virtual canvas information stored in the cropped image.
Input:
bboxArr=(`convert circle_rectangle_transp.png -type bilevel \
-define connected-components:verbose=true \
-define connected-components:exclude-header=true \
-define connected-components:mean-color=true \
-connected-components 8 null: | grep "gray(0)" | awk '{print $2}'`)
echo "${bboxArr[*]}"
i=0
for bbox in ${bboxArr[*]}; do
echo $bbox
convert circle_rectangle_transp.png -crop "$bbox" -background none -flatten circle_rectangle_transp_$i.png
i=$((i+1))
done
I'd like to get the coordinates of all areas containing any text in scans of documents like the one shown below (in reduced quality; the original files are of high resolution):
I'm looking for something similar to these (GIMP'ed-up!) bounding boxes. It's important to me that the paragraphs be recognized as such. If the two big blocks (top box on left page, center block on right page) would get two bounding boxes each, though, that would be fine:
The way of obtaining these bounding box coordinates could be through some kind of API (scripted languages preferred over compiled ones) or through a command line command, I don't care. What's important is that I get the coordinates themselves, not just a modified version of the image where they're visible. The reason for that is that I need to calculate the area size of each one of them and then cut out a piece at the center of the largest.
What I've already tried, so far without success:
ImageMagick - it's just not meant for such a task
OpenCV - either the learning curve is too high or my google-foo too bad
Tesseract - from what I've been able to garner, it's the one-off OCR software that, for historical reasons, doesn't do Page Layout Analysis before attempting character shape recognition
OCRopus/OCRopy - should be able to do it, but I'm not finding out how to tell it I'm interested in paragraphs as opposed to words or characters
Kraken ibn OCRopus - a fork of OCRopus with some rough edges, still fighting with it
Using statistics, specifically, a clustering algorithm (OPTICS seems to be the one most appropriate for this task) after binarization of the image - both my maths and coding skills are insufficient for it
I've seen images around the internet of document scans being segmented into parts containing text, photos, and other elements, so this problem seems to be one that has academically already been solved. How to get to the goodies, though?
In Imagemagick, you can threshold the image to keep from getting too much noise, then blur it and then threshold again to make large regions of black connected. Then use -connected-components to filter out small regions, especially of white and then find the bounding boxes of the black regions. (Unix bash syntax)
convert image.png -threshold 95% \
-shave 5x5 -bordercolor white -border 5 \
-blur 0x2.5 -threshold 99% -type bilevel \
-define connected-components:verbose=true \
-define connected-components:area-threshold=20 \
-define connected-components:mean-color=true \
-connected-components 4 \
+write tmp.png null: | grep "gray(0)" | tail -n +2 | sed 's/^[ ]*//' | cut -d\ -f2`
This is the tmp.png image that was created. Note that I have discarded regions smaller than 20 pixels in area. Adjust as desired. Also adjust the blur as desired. You can make it larger to get bigger connected regions or smaller to get closer to individual lines of text. I shaved 5 pixels all around to remove spot noise at the top of your image and then padded with a border of 5 pixels white.
This is the list of bounding boxes.
Here is the listing:
267x223+477+123
267x216+136+43
48x522+413+0
266x86+136+317
266x43+136+410
266x66+477+404
123x62+479+346
137x43+142+259
117x43+486+65
53x20+478+46
31x20+606+347
29x19+608+48
26x18+716+347
26x17+256+480
25x17+597+481
27x18+716+47
21x17+381+240
7x7+160+409
We can go one step further an draw boxes about the regions:
boxes=""
bboxArr=(`convert image.png -threshold 95% \
-shave 5x5 -bordercolor white -border 5 \
-blur 0x2.5 -threshold 99% -type bilevel \
-define connected-components:verbose=true \
-define connected-components:area-threshold=20 \
-define connected-components:mean-color=true \
-connected-components 4 \
+write tmp.png null: | grep "gray(0)" | sed 's/^[ ]*//' | cut -d\ -f2`)
num="${#bboxArr[*]}"
for ((i=0; i<num; i++)); do
WxH=`echo "${bboxArr[$i]}" | cut -d+ -f1`
xo=`echo "${bboxArr[$i]}" | cut -d+ -f2`
yo=`echo "${bboxArr[$i]}" | cut -d+ -f3`
ww=`echo "$WxH" | cut -dx -f1`
hh=`echo "$WxH" | cut -dx -f2`
x1=$xo
y1=$yo
x2=$((xo+ww-1))
y2=$((yo+hh-1))
boxes="$boxes rectangle $x1,$y1 $x2,$y2"
done
convert image.png -fill none -strokewidth 2 -stroke red -draw "$boxes" -alpha off image_boxes.png
Increase the threshold-area from 20 a little more and you can get rid of the tiny box on the lower left side around a round dot, which I think is noise.
I have ~200 scanned photos which I want to crop the white space out of. See example:
Can someone provide me with the appropriate command line code to do this?... I have been trying to sort out the -trim and -fuzz options with no luck. NOT ALL images are same size (i.e. 4x6, 5x7, etc). All images were scanned/saved as jpg
Ideal scenario is a script where new trimmed photos are saved in one subdirectory.
Thanks in advance!
I would suggest using -morphology to remove the scan artifacts, trim, and then capture the resulting paging.
PAGE_OFFSET=$(convert TrmkF.jpg -morphology Dilate:3 Diamond:3,5 -fuzz 10% -trim -format '%wx%h%O' info:-)
The $PAGE_OFFSET variable should now have the rough location of the scanned photo. We can apply that value with the -crop command.
convert TrmkF.jpg -crop $PAGE_OFFSET output.jpg
[![output][1]][1]
Edit
A (powershell) batch script may look as simple as...
Get-ChildItem "C:\path\to\photos" -Filter *.jpg |
Foreach-Object {
$pageOffset = magick $_.FullName -morphology Dilate:3 Diamond:3,5 -fuzz 10% -trim -format '%xx%h%O' info:- | Out-String
$output = $_.FullName + ".output.jpg"
magick $_.FullName -crop $pageOffset +repage $output
}
ymmv
[1]: https://i.stack.imgur.com/u8bSs.png
I've found that the above gives bad results, I think the formatting is different on MacOS or something so sharing the success story here. I have exactly this same issue - hundreds of scanned photos with some blotches in the white ruining the auto trim function.
I just modified parameters from the other individual's answer and got amazing results using this:
cd into your folder of images
mkdir ../done
v
echo "$f";\
size=$(magick "$f" -bordercolor White -border 10x10 \
-morphology Dilate:5 Diamond:5,7 -fuzz 5% -trim \
-format "%wx%h%O" info:-); \
echo $size; \
magick "$f" -bordercolor White -border 10x10 -crop $size +repage "../done/$f"; done;
im getting the most existing colors of an image and display it with the "histogram" funktion like
convert image.jpg -scale 100x100 -gravity \
center -crop 50% -dither None -remap color_map.gif \
-format %c histogram:info:
22: ( 0, 0, 0) #000000 black
881: (119,133,142) #77858E rgb(119,133,142)
268: (186, 84, 29) #BA541D rgb(186,84,29)
662: (212,212,212) #D4D4D4 grey83
20: (244,203, 98) #F4CB62 rgb(244,203,98)
647: (255,255,255) #FFFFFF white
Ho can i work now with this output? i want to save the most existing color in my database, but i dont know how to get now only the color with the number 881.
Can any one help me?
If you want to do this purely from the shell (assuming Bash-like Unix environment), something like this would work:
convert image.jpg -scale 100x100 -gravity center \
-crop 50% -dither None -remap color_map.gif \
-format %c histogram:info: | sort | tail -n1 | \
sed -e 's/.*\(#[0-9A-F]\+\).*/\1/'
That takes the output from ImageMagick, sorts it so the largest color count is on the bottom, takes just that line, then extracts the color hex from the line. You can tweak the sed regex if your goal is to get the decimal rgb values instead of the hex.
So for your example image, the output should be just:
#77858E
I'm looking for the most efficient way to stitch multiple images together in ImageMagick, on top of a background image, such that the spacing / padding between the overlaid images is consistent?
I've investigated use of +append, convert -composite, and convert with -page and -layers merge.
The following command (convert -composite) works, but requires precalculation of image dimensions in order to specify absolute offsets. Really, I want a 10 pixel gap between the end of the FIRST layered image and the start of the second layered image, but the only way I can see to achieve that is by specifying the absolute offset from the top-left corner of the canvas.
convert \
background.jpg \
first.jpg -gravity Northwest -geometry +10+10 -composite \
second.jpg -geometry +300+10 -composite \
third.jpg -geometry +590+10 -composite \
output.jpg
I am looking for some sort of operator so that the horizontal offset can be interpreted relative to the "last" image in the layering, so instead of specifying +300+10 for the second image and +590+10 for the third, I can somehow specify a +10+10 offset.
I thought gravity would allow me to achieve that (-gravity Northwest), in the same way that float: left; works in CSS positioning, but that is not the case.
I have also had some success with the following:
convert \
-page +10+10 first.jpg \
-page +300+10 second.jpg \
-page +590+10 third.jpg \
-background transparent \
-layers merge \
layered.png
convert background.jpg layered.png -gravity Center -composite output.jpg
Both the techniques described require pre-calculation of absolute offsets, which is a bit of a pain. Is there a better way to do this?
You've overlooked the montage command.
The most simple command to add the wanted spacing with it would be to set a -frame 5 option with -mattecolor none. This works with images of different width values and spaces them all apart with a distance of 10 pixels:
montage \
-alpha on \
-background none \
-mode concatenate \
-tile x1 \
-frame 5 \
-mattecolor none \
*.jpg \
output1.png
You'll easily notice however, that the resulting image's border is only 5 pixels wide on top, right, bottom and left. To remove these 5 pixels all around use:
convert output1.png -shave 5 output2.png
To overlay this result on your background.jpg, use:
convert \
background.jpg \
output2.png \
-gravity Northwest \
-geometry +10+10 \
-composite \
final.jpg
You can also use Kurt's transparent frame trick with append. Using append instead of montage has the advantage that you can use gravity settings to align your images top (north) bottom (south) or center.
Here's an example of how to append images horizontally with a 10 pixel gap between them, and with the images top aligned:
convert \
-frame 5 \
-mattecolor none \
-background none \
-gravity north \
first.jpg second.jpg third.jpg \
+append \
png:- | convert - -shave 5
output.png
To append images vertically use -append instead of +append. I've used a pipe | to shave off the outside frame in the same command.