1

Sorry, I don't have the right terminology to ask this question, but in simple terms, I have several images like this one. The white areas are not always the same size but are mostly rectangular. The colors are always the same. It's one image and I need to isolate the figures from the white background, or explained in a different way, I need to change the black background to white...

enter image description here

and make it look like this.

enter image description here

Note some of the figures are touching the edges where white meets the black.

The tool, library, or programming language doesn't matter as long as it gets it done.

Mack
  • 149
  • 1
  • 8
  • 1
    How many images like this do you have? Are the white boxes always the same size and in the same place? What OS do you use? What have you tried? Are the colours always black and white, or might they be red and yellow? In general, if you want someone to help you, it's a good idea to make it easy for them... – Mark Setchell Jan 14 '18 at 17:58
  • I would ask the same questions as Mark Setchell. Is this one image with a black background or 3 images with the black curve inside a white background? Also if you have Imagemagick, what is your version and platform, since syntax may vary. – fmw42 Jan 14 '18 at 20:34
  • I have updated my question and I'm using ImageMagick 7. – Mack Jan 14 '18 at 21:25
  • Is it ok if you get 3 separate images back as the output? Or is the spacing between the figures significant? – Mark Setchell Jan 14 '18 at 22:14
  • @MarkSetchell Yes, spacing is important. – Mack Jan 14 '18 at 22:37

9 Answers9

3

Here's another way that may work for your other images... clone, then dilate (enlarge) the little white windows by 7 pixels, then erode them again, invert and choose the lightest pixels:

convert image.png \( +clone -morphology dilate square:7x7 -morphology erode square:7x7 -negate \) -evaluate-sequence max  result.png

enter image description here

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • Very neat, Mark! – fmw42 Jan 15 '18 at 18:38
  • Thank you! I will study carefully the details of each answer and will pick one as the "best" answer. – Mack Jan 15 '18 at 22:56
  • I haven't been able to go in depth over every example yet but I have learned quite a bit from the detailed answers with descriptions on every step. I really appreciate you both. I'll pick this one because of simplicity and because it also works on a grid of aligned white boxes as well. The concept of image manipulation is not foreign to me but it's not that simple. Where can I learn about strategies or methods of image manipulation to solve problems such as this one. Can you mention some books or resources? – Mack Jan 17 '18 at 15:39
  • 1
    I would do a Google search for "image processing books". The one from Gonzalez and Woods is very popular. See https://www.amazon.com/Digital-Image-Processing-Rafael-Gonzalez/dp/0133356728. If using Imagemagick, then see https://www.imagemagick.org/script/command-line-processing.php and https://www.imagemagick.org/Usage/reference.html and https://www.imagemagick.org/Usage/ – fmw42 Jan 17 '18 at 17:44
2

Here is one method that works for this image using Imagemagick. You trim the black area first, then divide the image into 3 equal sections, then trim those, then flatten onto white. The trim and crop keep track of the original offsets since I do not use +repage to remove the virtual canvas.

magick original.png -fuzz 5% -trim -crop 3x1@ -trim -background white -flatten result.png

enter image description here

See https://www.imagemagick.org/Usage/crop/#crop_equal https://www.imagemagick.org/Usage/crop/#trim

fmw42
  • 46,825
  • 10
  • 62
  • 80
  • This method of trimming works on this particular image, but what happens when spacing is uneven? Is there a better way to do this without the trimming and cropping? – Mack Jan 15 '18 at 02:11
2

Interesting question! I assume you realise your little white windows into your symbols are not horizontally aligned? We'll come to that later. I have a fun method based on a "squeezebox", or accordion...

enter image description here

There are 3 parts, so I'll do a horizontal rule under each to divide them up.


Part 1

Squeeze your image in together from the left and right sides (like the squeezebox) till it is only a single pixel wide column but the same height as the original:

enter image description here

Now threshold the column so that only horizontal lines that were fully black stay black. Anywhere there was a window or any white pixels on a horizontal line it becomes white. Then invert it.

enter image description here

Now, stretch the column (extend that squeezebox) back to its original size using nearest neighbour sampling:

enter image description here


Part 2

Now do the same thing again, but after rotating the squeezebox through 90 degrees. Basically, we are going to squash the image till it is 1 pixel high, threshold and negate it, then stretch (extend that vertically rotated squeezebox) till it is back to the original height:

enter image description here


Part 3

Now take the original image and put in a stack with the two black and white stripey ones above and, at each pixel location, choose the lightest pixel:

enter image description here

enter image description here

enter image description here

The code looks like this:

#!/bin/bash

# Get width and height
read w h < <(identify -format "%w %h" image.png)
echo $w $h

magick image.png -threshold 50% \
   \( -clone 0 -resize 1x\! -threshold 1% -negate -scale ${w}x${h}\! \) \
   \( -clone 0 -resize x1\! -threshold 1% -negate -scale ${w}x${h}\! \) \
   -evaluate-sequence max result.png

enter image description here


Ok, that's not bad and it doesn't rely on there being exactly 3 windows. But, as I said at the start, the little white windows are not aligned across their tops and so you get artefacts around the windows - only in the horizontal direction because there is only one image in the vertical direction so nothing to align. Basically, I suggest to do a little morphology to make your windows all 3 pixels smaller in height top and bottom, the code then looks like this:

#!/bin/bash

# Get width and height
read w h < <(identify -format "%w %h" image.png)
echo $w $h

magick image.png -threshold 50% \
   \( -clone 0 -resize 1x\! -threshold 1% -negate -scale ${w}x${h}\! -morphology dilate rectangle:7x7 \) \
   \( -clone 0 -resize x1\! -threshold 1% -negate -scale ${w}x${h}\!                                  \) \
   -evaluate-sequence max result.png

enter image description here

Change the rectangle:7x7 to larger numbers to trim more pixels around your window edges. Basically it will trim half the number of pixels off the top and bottom. So rectangle:7x7 will make your window 3 pixels smaller at the top and bottom, whereas rectangle:15x15 will make them 7 pixels smaller. Here's a link to Anthony Thyssen's excellent pages on how to use ImageMagick - and morphology in particular.

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • 1
    That is a good method, Mark, but it likely would fall apart if the white boxes were rotated much. – fmw42 Jan 15 '18 at 18:26
  • @fmw42 Thank you. Given that OP has only provided a single image, I am not certain of the constraints or likely variations, but I agree that some of my other answers are likely (hopefully) more robust :-) – Mark Setchell Jan 15 '18 at 19:18
  • `@Mark Setchell` Yes, I agree. Your other two methods are much more robust. The one is quite simple and elegant. – fmw42 Jan 15 '18 at 21:43
2

Here's another idea...

convert image.png -threshold 50% \
   \( +clone -morphology edgeout square:3x3 -write step1.png \
      -fill red -draw 'color 0,0 floodfill' -write step2.png \
      -fill black +opaque red -fill black   -write step3.png \
      -opaque red                           -write step4.png \
      -morphology dilate square:3x3         -write step5.png \
   \) -evaluate-sequence max result.png

And here are the steps 1-5 and the result. You can remove all the -write stepX.png parts from the code, they are just so you can see what I am doing:

Step1 - "show me all the pixels around the edges of the white areas"

enter image description here

Step 2 - "flood fill with red from the top-left corner now we have proper edges to our windows so that the flood doesn't "leak" into the windows"

enter image description here

Step 3 - "make everything that is not red into black"

enter image description here

Step 4 - "make everything that is red into white"

enter image description here

Step 5 - "make the white shapes a fraction larger"

enter image description here

Result - "at every pixel location, pick the pixel that is lightest out of the current and original image"

enter image description here

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
2

Mark's morphology method is a little simpler than this. So I suggest going with it as the best method so far. But here is a similar method (kind of a hybrid of his last two methods) that may be of interest. I have include +write tmpX.png to show the steps. Those can be removed. (Unix Syntax using Imagemagick 6.9.9.33)

convert original.png \
\( -clone 0 -morphology edgeout square:3 -negate +write tmp1.png \) \
\( -clone 0 -morphology dilate square:3 \
-morphology edge square:3 +write tmp2.png \) \
-delete 0 \
-evaluate-sequence max \
result.png

Line 1: reads the input

enter image description here

Line 2 - tmp1.png: uses morphology edgeout to convert the image to black curves with black box around them on a white background (as in Mark's other example).

enter image description here

Line 3 and 4 - tmp2.png: uses morphology dilate (to remove the curves) and edge to just create a white outline box around the white areas on a black background.

enter image description here

Line 6 and 7: result.png removes the black box in the tmp1.png using tmp2.png by taking the maximum between the two images pixel-by-pixel.

enter image description here

For comparison, here is Mark's elegant method that I have simplified a little:

convert original.png \
\( +clone -morphology close square:3 -negate +write tmp1.png \) \
-evaluate-sequence max \
result.png

Line 1: read the input

enter image description here

Line 2: - tmp1.png: use morphology close (same as dilate and erode) to create a black box to replace the white area including the black curve in the original. The square size can be as low as 3, but not lower for this to work. The smaller the less is removed from the curve ends.

enter image description here

Line 3: remove all the black in the original other than the curves by computing the maximum between the two image pixel-by-pixel.

enter image description here

fmw42
  • 46,825
  • 10
  • 62
  • 80
1

@Mack wrote:

This method of trimming works on this particular image, but what happens when spacing is uneven? Is there a better way to do this without the trimming and cropping?

One way would be to separate each white square from the background into individual images. I have a bash unix shell script, multicrop2, that will do that. But for this image, it will produce one extra large image. For example:

magick original.png tmp.png
multicrop2 -b white tmp.png result.png

enter image description here

enter image description here

enter image description here

enter image description here

You can discard the last one shown here.

See my script, multicrop2, at http://www.fmwconcepts.com/imagemagick/index.html

As is currently, the script loses the virtual canvas of each output image. But I might be able to add an argument to keep it, so that the 3 good images could be flattened onto white such that the separate images would be merged back into a white background at there proper locations.

fmw42
  • 46,825
  • 10
  • 62
  • 80
  • Thank you for your help. Yes, I would like to keep the virtual canvas if possible. All in all, the new image should have the same dimensions as the original, as my second picture shows. An alternative to this perhaps would be to add the offsets or coordinates to the file names of the three files generated, but keeping those figures in the virtual canvas at the proper location is what I'm after. – Mack Jan 15 '18 at 04:54
  • 1
    I tried a quick modification of the script and it only works properly after I trim the image as above. Thus it would not remember the original size. So I do not have a good way to deal with this at this time. Perhaps, I can find a way, but I will have to think about it further. It would have been much easier if the background were another color or if the white completely surrounded the curves. – fmw42 Jan 15 '18 at 05:40
0

If by this you mean replace the white by transparency (a.k.a. color-to-alpha in image editors), the magical formula is:

convert original.png ( -clone 0 -fill "#a0132e" -colorize 100 ) ( -clone 0,1 -compose difference -composite -separate +channel -evaluate-sequence max -auto-level ) -delete 1 -alpha off -compose over -compose copy_opacity -composite output.png

Explanations here

xenoid
  • 8,396
  • 3
  • 23
  • 49
0

Here's a slightly different solution using gaussian blur as a morphological operator. I used pyvips, but it would be easy to redo in magick.

The idea is that if you gaussblur with a fairly large sigma and then threshold > 0, you'll expand all the white by the gaussian radius and thereby fill in the lines.

The neat bit is that this can be used a second time with the threshold flipped to erode by exactly the amount you dilated by. Just blur a second time by the same amount, but now do != 255 and you'll have the shape of the background! Bitwise OR that with your original image for a solution.

import sys
import pyvips

im = pyvips.Image.new_from_file(sys.argv[1], access="sequential")

bg = im.gaussblur(2) > 0
bg = bg.gaussblur(2) != 255

im |= bg

im = im.write_to_file(sys.argv[2])

Then:

python characters.py ~/pics/characters.png x.png

Makes:

result image

One nice feature of this is that there's no flooding, so you can stream the image. It'll work on images of any size and need very little memory.

jcupitt
  • 10,213
  • 2
  • 23
  • 39
0

Here is another rather simple morphology method (bottomhat) using Imagemagick. The kernel size needs to be at least 4 in this case. See https://www.imagemagick.org/Usage/morphology/#bottom-hat

Input:

enter image description here

convert source.png -morphology bottomhat octagon:4 -negate result.png

enter image description here

fmw42
  • 46,825
  • 10
  • 62
  • 80