How to identify fully formed rectangles? - imagemagick

I'm trying to identify the coordinates of the reactangles that are fully formed. I mean, that have 4 sides with white border line making fully box.
This is the input image I have.
In the below image I show, in yellow, the rectangles for which I'd like to get the coordinates.
In this input image there are 3 black rectangles with white border line and 1 rectangle that is all white.
My current convert code gives coordinates of all areas, including those white areas that generate noise for my purpose.
convert input.png \
-define connected-components:verbose=true \
-define connected-components:area-threshold=100 \
-connected-components 8 -auto-level out:null | grep "255,255,255"
7602: 233x81+295+192 411.0,232.0 18873 srgb(255,255,255)
31: 356x70+365+28 542.4,57.2 4602 srgb(255,255,255)
7604: 538x510+45+273 163.1,529.1 4394 srgb(255,255,255)
7605: 292x470+627+273 809.5,494.2 2116 srgb(255,255,255)
1393: 149x45+785+40 860.8,60.5 2040 srgb(255,255,255)
8449: 513x125+70+658 326.0,708.6 761 srgb(255,255,255)
7015: 43x27+291+110 312.5,122.1 620 srgb(255,255,255)
7599: 84x43+676+148 717.5,169.0 250 srgb(255,255,255)
So, my question is: is there a way to identify from the output given by convert command, which coordinates belong to rectangles fully formed? Thanks

A couple of ideas spring to mind. I haven't developed them into full solutions but may do so if time permits later.
You could maybe choose the centre of each connected component in your list as the seed point for a flood-fill with, say yellow, and then make everything not yellow black (with -fill black +opaque yellow) and run connected components again to see if you get a filled shape the correct area. So, for example, choosing your 4th output line:
7604: 538x510+45+273 163.1,529.1 4394 srgb(255,255,255)
And flood filling from the centre:
magick outlines.png -fill yellow -floodfill +314+478 black result.png
Or maybe go a little further:
magick outlines.png -fill yellow -floodfill +314+478 black -fill black +opaque yellow result.png
Then run another connected components analysis and see if you get a fully yellow-filled shape detected.
You could maybe run a Hit-or-Miss morphology, looking for line ends and follow them back to T-junctions and erase them to get rid of the "overshoot" lines that stick out beyond the ends of your rectangles.
By the way, if you are looking specifically for rectangles, you will probably be better off checking for 4-connected components rather than 8-connected as at present.

Related

ImageMagick and segmenting contiguous islands in a transparent background image

I'm trying to convert something that looks like this into a bunch of smaller image crops or "slices", where each slice might overlap with another, but should contain each island of non-transparent pixels. The long continuous strokes don't have to be converted, but most of the "spots" should be.
So effectively, I'm trying to automatically "slice" it to look some thing like this (rough manual slicing):
How do you use imagemagick to automatically crop the elements of such an image, and to also return the position, width/height that each sub-image was cropped from?
You can use "Connect Component Analysis". As your image contains no transparency it is hard to use, so I hacked it and coarsely made the chessboard areas transparent like this:
Then do something along these lines:
magick image.png -alpha off -colorspace gray -negate -threshold 10% \
-define connected-components:verbose=true \
-define connected-components:area-threshold=100 \
-connected-components 8 -auto-level output.png
Sample Output
Objects (id: bounding-box centroid area mean-color):
1: 498x614+0+0 236.9,317.2 219166 srgb(0,0,0)
2: 112x583+112+0 177.0,268.6 16094 srgb(255,255,255)
27: 128x210+350+342 403.8,436.5 14195 srgb(255,255,255)
7: 104x148+338+85 390.0,160.4 10451 srgb(255,255,255) <--- this one
3: 103x145+200+0 250.9,70.9 10317 srgb(255,255,255)
25: 107x132+202+245 252.7,307.8 9227 srgb(255,255,255)
36: 117x139+213+428 268.3,497.2 9103 srgb(255,255,255)
12: 139x77+0+112 62.1,152.4 7162 srgb(255,255,255)
16: 73x93+414+161 459.0,213.4 4104 srgb(255,255,255)
37: 51x61+249+457 274.6,488.1 2224 srgb(0,0,0)
4: 71x36+338+0 382.0,19.6 1507 srgb(255,255,255)
26: 32x47+238+277 253.1,300.2 875 srgb(0,0,0)
43: 50x30+283+584 310.7,600.0 833 srgb(255,255,255)
13: 41x17+31+140 51.0,148.1 410 srgb(0,0,0)
9: 11x11+174+100 178.7,105.3 104 srgb(255,255,255)
Each line of output corresponds to a blob. There is a header at the start that tells you what each field is.
Let's look at the line:
7: 104x148+338+85 390.0,160.4 10451 srgb(255,255,255)
and draw that blob into your image in semi-transparent red:
magick image.png -fill "rgba(255,0,0,0.5)" -draw "rectangle 338,85 442,243" result.png
Note that I have actually discarded the alpha channel (with -alpha off) in my command and used the RGB channels converted to greyscale as items to detect. Depending how your image is created, you may be better of discarding the RGB channels and just using the alpha or its inverse (-alpha extract -negate).
Not sure ImageMagick can do that.
There is a script that does something similar for Gimp: ofn-extract-objects.
The technique it uses is fairly simple: select the background and convert the selection to "paths" (ie, vectors). The vectors will have one "stroke" per continuous border in the selection (this means that you have nested strokes if you have objects with holes, but this isn't too hard to detect and filter). Once you have the strokes you can use the coordinates of the anchors and tangents to determine their bounding box.
If you don't want to use Gimp, you can use potrace and process the output SVG in a similar way.

ImageMagick - Trim / Crop to contiguous objects

How do you do the equivalent of this step in Photoshop.
https://gyazo.com/180a507c0f3c9b342fe33ce218cd512e
Supposed there are two contiguous objects in an image, and you want to create exact sized crops around each one and output as two files. (Generalize to N files)
You can do that with "Connected Component Analysis" to find the contiguous blobs.
Start Image
convert shapes.png -colorspace gray -negate -threshold 10% \
-define connected-components:verbose=true \
-connected-components 8 -normalize output.png
Sample Output
Objects (id: bounding-box centroid area mean-color):
0: 416x310+0+0 212.3,145.2 76702 srgb(0,0,0)
1: 141x215+20+31 90.0,146.2 26129 srgb(255,255,255)
2: 141x215+241+75 311.0,190.2 26129 srgb(255,255,255)
Notice how each blob, or contiguous object, is "labelled" or identified with its own unique colour (shade of grey).
So there is a header line telling you what the fields are followed by 3 blobs, i.e. one per line of output. The first line is the entire image and not much use. The second one is 141 px wide and 215 px tall starting at +20+31 from the top-left corner. The third one is the same size (because I copied the shape) and starts as +241+75 from the top-left corner.
Now stroke red around the final indicated rectangle - bearing in mind that rectangle takes top-left and bottom-right corners rather than top-left corner plus width and height.
convert shapes.png -stroke red -fill none -draw "rectangle 241,75 382,290" z.png
And crop it:
convert shapes.png -crop 141x215+241+75 z.png
And here is the extracted part:
If you want to generalise, you can just pipe the ImageMagick output into awk and pick out the geometry field:
convert shapes.png -colorspace gray -negate -threshold 10% -define connected-components:verbose=true -connected-components 8 -normalize output.png | awk 'NR>2{print $2}'
Sample Output
141x215+20+31
141x215+241+75

Finding the Largest Connected Blob Starting from Selected Point

I am trying to extract a region from an image that is already marked with a certain color. In the picture below
I would like to extract only the pixels which belong to the sidewalk, that is, all pixels that belong to the black blob that is connected to the mid-lower part of the image. There are black dots outside that blob which I am not interested in. So if I could get roughly the region shown below
it would be perfect. Does anyone know of some common algorithms that can do this? Morphology? Region growing using a kind of flooding algorithm?
Thanks,
You can do that quite easily with a flood fill. If I use ImageMagick to demonstrate at the command line because it is installed on most Linux distros and is available for macOS and Windows.
So, bearing in mind that the pixel you identified as your seed is at around 440,520 in the image you supplied that includes the axes, we can floodfill all pixels that match that colour and touch the seed with cyan using:
convert scene.png -fill cyan -draw 'color 440,520 floodfill' result.png
Or, we can make a mask by changing the non-cyan pixels to white and the cyan pixels to black:
convert scene.png -fill cyan -draw 'color 440,520 floodfill' -fill white +opaque cyan -fill black -opaque cyan z.png
There are a thousand other things you can simply do from the command line to take this further... fill small holes in the mask, make a transparency layer from the mask - just ask more questions if you need a hand.
If you want to close the holes in your image, you probably want to use morphological functions. I am away from any computers with ImageMagick for a week so I can only tell you in general terms. Start with the pure black and white (no grey) picture above and try:
convert image.png -morphology open disk:3 result.jpg
Try replacing the word open above with close, erode or dilate. Experiment with disk, disk:3 disk:7 and so on.

How can I detect edges on an image of a document, and cut sections into seperate images?

The task is to take an image of a document, and leverage straight lines surrounding different 'sections' in order to split up the image into different documents for further parsing. Size of the different 'sections' is completely variable from page to page (we're dealing with several thousand pages). Here is an image of what one of these images looks like:
Example of how the documents are laid out:
Image analysis/manipulation is completely new to me. So far I've attempted to use Scikit-image edge detection algorithms to find the 'boxes', with hopes to use those 'coordinates' to cut the image. However, the two algorithms I've tried (Canny, Hough) are picking up lines of text as 'edges' on high sensitivity, and not picking up the lines that I want on low sensitivity. I could write something custom and low level to detect the boxes myself, but I have to assume this is a solved problem.
Is my approach headed in the right direction? Thank you!
You don't seem to be getting any OpenCV answers, so I had a try with ImageMagick, just in the Terminal at the command-line. ImageMagick is installed on most Linux distros and is available for macOS and Windows for free. The technique is pretty readily adaptable to OpenCV so you can port it across if it works for you.
My first step was to do a 5x5 box filter and threshold at 80% to get rid of noise an scanning artefacts and then invert (probably because I was planning on using morphology, but didn't in the end).
convert news.jpg -depth 16 -statistic mean 5x5 -threshold 80% -negate z.png
I then ran that through "Connected Components Analysis" and discarded all blobs with too small an area (under 2000 pixels):
convert news.jpg -depth 16 -statistic mean 5x5 -threshold 80% -negate \
-define connected-components:verbose=true \
-define connected-components:area-threshold=2000 \
-connected-components 4 -auto-level output.png
Output
Objects (id: bounding-box centroid area mean-color):
110: 1254x723+59+174 686.3,536.0 901824 srgb(0,0,0)
2328: 935x723+59+910 526.0,1271.0 676005 srgb(0,0,0)
0: 1370x1692+0+0 685.2,712.7 399651 srgb(0,0,0)
2329: 303x722+1007+911 1158.0,1271.5 218766 srgb(0,0,0)
25: 1262x40+54+121 685.2,140.5 49820 srgb(255,255,255)
109: 1265x735+54+168 708.3,535.0 20601 srgb(255,255,255)
1: 1274x64+48+48 675.9,54.5 16825 srgb(255,255,255)
2326: 945x733+54+905 526.0,1271.0 16660 srgb(255,255,255)
2327: 312x732+1003+906 1169.9,1271.5 9606 srgb(255,255,255) <--- THIS ONE
421: 403x15+328+342 528.6,350.1 4816 srgb(255,255,255)
7: 141x23+614+74 685.5,85.2 2831 srgb(255,255,255)
The fields are labelled in the first line, but the interesting ones are the second (block geometry) and fourth field (blob area). As you can see, there are 11 lines so it has found 11 blobs in the image. The second field, AxB+C+D means a rectangle A pixels wide by B pixels tall with its top-left corner C pixels from the left edge of the image and D pixels down from the top.
Let's look at the one I have marked with an arrow, which starts 2327: 312x732+1003+906 and draw a rectangle over that one:
convert news.jpg -fill "rgba(255,0,0,0.5)" -draw "rectangle 1003,906 1315,1638" oneArticle.png
If you want to crop that article out into a new image:
convert news.jpg -crop 312x732+1003+906 article.jpg
If we draw in all the other boxes , we get:

imagemagick detect coordinates of transparent areas

I have a PNG image with transparent areas, squares/rectangle areas that contains transparency.
I would like to know if there is some way that i can know the top,lef,width,height of theses transparent areas in the image.
Thanks for any help
Updated Answer
In the intervening years, I have come across a simpler solution, so I thought I would update it so anyone else seeing it can get the benefit of the latest and greatest.
Start off just the same, by extracting the alpha layer to an image of its own and invert it:
convert start.png -alpha extract -negate intermediate.png
Now perform a "Connected Component Analysis" on that:
convert start.png -alpha extract -negate \
-define connected-components:verbose=true \
-define connected-components:area-threshold=100 \
-connected-components 8 -auto-level result.png
Objects (id: bounding-box centroid area mean-color):
0: 256x256+0+0 128.7,130.4 62740 srgb(0,0,0)
3: 146x8+103+65 175.5,68.5 1168 srgb(255,255,255)
2: 9x93+29+42 33.0,88.0 837 srgb(255,255,255)
1: 113x7+4+21 60.0,24.0 791 srgb(255,255,255)
You will see there is a header line and 4 lines of output and each has a colour at the end, the first line is black, and corresponds to the entire shape, and the the last three are white, corresponding to the three transparent areas. It is basically the second field on each of the last three lines that you want. So, 146x8+103+65 means a box 146px wide by 103px tall offset 103px to the right of the top-left corner, and 65px down from the top-left corner.
If I draw those in, in red, you can see what it has identified:
convert result.png -stroke red -fill none -strokewidth 1 \
-draw "rectangle 103,65 249,73" \
-draw "rectangle 29,42 38,135" \
-draw "rectangle 4,21 117,28" result.png
Original Answer
The following may help you get to an answer but I have not developed it all the way through to completion - people often ask questions and then never log in again and there is quite a lot of effort involved...
Let's start with this input image - where the white areas are transparent:
You can extract the alpha channel from an image with ImageMagick like this:
convert input.png -alpha extract -negate alpha.png
which gives this, where white areas are transparent
Ok, one approach is to find the bounding box of the white areas, you can do this with trim and it will give you the bounding box that encloses the white areas:
convert input.png -alpha extract -format "%#" info:
245x114+4+21
So the bounding box is 245px wide and 114px high starting at offset +4+21 from top left. I can draw that on the image to show it:
So, that is a start.
Also, you can get ImageMagick to enumerate the pixels in text format, so you can run this command
convert input.png -alpha extract -negate txt: | more
# ImageMagick pixel enumeration: 256,256,255,gray
0,0: (0,0,0) #000000 gray(0)
1,0: (0,0,0) #000000 gray(0)
2,0: (0,0,0) #000000 gray(0)
which tells you that the image is 256x256 and that the first 3 pixels are all black. If you want the white ones (i.e. transparent ones) you can do this:
convert input.png -alpha extract -negate txt: | grep FFFFFF | more
4,21: (255,255,255) #FFFFFF gray(255)
5,21: (255,255,255) #FFFFFF gray(255)
6,21: (255,255,255) #FFFFFF gray(255)
7,21: (255,255,255) #FFFFFF gray(255)
This tells you that pixel 4,21 is the top left corner of your transparent area - I'm glad it matches the output from the bounding box method above :-)
So, you can easily get a list of all the pixels that are transparent. This approach could be developed, or something similar coded up in Ruby (RMagick) to find contiguous areas of black - but that is beyond the scope of this answer for the moment - as I am not a Ruby programmer :-)
Ok, I have learned some Ruby this afternoon and, no laughter please, this is my first Ruby program. It is probably pretty ugly and more like Perl or C (my preferred languages) but it works and finds the rectangular transparent areas.
#!/usr/bin/ruby
require 'RMagick'
include Magick
infile=ARGV[0]
img = ImageList.new(infile)
w=img.columns
h=img.rows
#Extract alpha channel into pixel array
px=img.export_pixels(0,0,w,h,"A")
for row in 0..h-1
for col in 0..w-1
thispx=px[w*row+col]
if thispx<32768 then
a=row
b=col
# Find extent (c) of rectangle towards right
for r in col..w-1
thispx=px[w*row+r]
if thispx<32768
c=r
else
break
end
end
# Find extent (d) of rectangle towards bottom
for s in row..h-1
thispx=px[w*s+col]
if thispx<32768
d=s
else
break
end
end
# Blank this rectangle as we have located it
for r in row..d
for s in col..c
px[w*r+s]=65535
end
end
# Tell caller about this rectangle
printf "%d,%d %d,%d\n",a,b,d,c
end
end
end
Run it like this:
bounds.rb input.png

Resources