skip to Main Content

I have the image below with almost all background in black.
enter image description here
Is there a way to crop rectangles surrounded by black without know the coordinates, were:

  1. height > 400px
  2. height about 144px
  3. height about 40px

I’m not interested in the blue rectangle (were height is about 45px) that is above each big rectangle.
enter image description here
I’d like to store the cropped rectangles in memory in order to make further processing with them. So for this
input image would be 4 images stored in memory like below. Thanks in advance for any help.

Image 1:

enter image description here

Image 2

enter image description here

Image 3:

enter image description here

Image 4:

enter image description here

UPDATE

Trying step by step Mark’s solution, I’m getting different output in this steps:

# Threshold image to get white regions of interest on black background
_, thr = cv.threshold(grey,0, 255, cv.THRESH_BINARY)
cv.imwrite('DEBUG-thr.png', thr)

I’ve tested changing to several different values like (0,127), (0,50), (127, 255), etc and I cannot get similar output to yours DEBUG-thr.png

This is what I get with (0,255) cv.threshold(grey,0, 255, cv.THRESH_BINARY)
enter image description here

This is my python and opencv version (running within WSL Ubuntu)

$ python
Python 3.12.0 (main, Oct 21 2023, 17:42:12) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> cv2.__version__
'4.8.1'
>>>

UPDATE2

I understand why the output is different for me. I tried your script with my actual input image (see below), that has gray background, and your script takes as input the black background image I show as input in this question. I’ve shown the black background image because I made some attempts previously to get my goal with imagemagick in bash.

enter image description here

2

Answers


  1. Per the discussion, you seem happy to go with an OpenCV solution, so I made you one. I didn’t pay too much attention to extracting the exact items you were looking for with all the criteria, I just extracted all the contours taller than 35px. You can dink around with the exact heights/widths/colours that interest you:

    #!/usr/bin/env python3
    
    import cv2 as cv
    import numpy as np
    
    # Load image and make greyscale version
    im = cv.imread('uvXcA.png')
    grey = cv.cvtColor(im, cv.COLOR_BGR2GRAY)
    cv.imwrite('DEBUG-grey.png', grey)
    
    # Threshold image to get white regions of interest on black background
    _, thr = cv.threshold(grey,0, 255, cv.THRESH_BINARY)
    cv.imwrite('DEBUG-thr.png', thr)
    
    # Morphology to get rid of small artefacts
    SE = cv.getStructuringElement(cv.MORPH_RECT, (3,3))
    cleaned = cv.morphologyEx(thr, cv.MORPH_CLOSE, SE)
    cv.imwrite('DEBUG-cleaned.png', cleaned)
    
    # Find "interesting" objects
    contours, _ = cv.findContours(cleaned, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
    for i,c in enumerate(contours):
       # Get bounding rect of this contour
       x,y,w,h = cv.boundingRect(c)
       # Print if taller than 35 px
       if h>35:
          print(f'Contour: {i}, {w}x{h} @ {x},{y}')
          # Extract from original colour image, not the one we morphed
          extracted = im[y:y+h, x:x+w]
          cv.imwrite(f'DEBUG-contour{i}.png', extracted)
    

    This is the output from the program:

    Contour: 4, 894x62 @ 1001,1521
    Contour: 5, 894x62 @ 70,1521
    Contour: 6, 892x630 @ 1002,876
    Contour: 9, 892x630 @ 71,876
    Contour: 10, 892x531 @ 1002,330
    Contour: 94, 892x531 @ 71,330
    Contour: 98, 428x144 @ 1467,155
    Contour: 99, 428x144 @ 1001,155
    Contour: 100, 429x144 @ 535,155
    Contour: 101, 428x144 @ 70,155
    Contour: 106, 564x42 @ 1331,110
    Contour: 107, 44x48 @ 0,101
    Contour: 108, 1920x81 @ 0,0
    

    These are the debug images in the order that the processing generates them, and also some of the extracted images:

    enter image description here

    enter image description here

    enter image description here

    enter image description here

    enter image description here

    enter image description here

    Login or Signup to reply.
  2. I can do the following in Imagemagick 7 to get each non-black-containing regions using connected components filtering.

    Threshold. Then get any region larger than 20000 pixels in area. Then keep only the white ones, i.e. gray(255). Then print only the bounding boxes of the regions. Then loop over each region and filter on height an also on standard deviation (to discard near constant regions). Then crop those regions that pass.

    cropboxArr=(`magick charts.png -alpha off 
    -threshold 1% -type bilevel 
    -define connected-components:area-threshold=20000 
    -define connected-components:mean-color=true 
    -define connected-components:exclude-header=true 
    -define connected-components:verbose=true 
    -connected-components 8 cleaned.png | grep "gray(255)" | awk '{print $2}'`)
    num=${#cropboxArr[*]}
    echo $num
    for ((i=0; i<num; i++)); do
    cropbox=${cropboxArr[$i]}
    echo "$i; $cropbox;"
    ww=`echo $cropbox | tr "x" "+" | cut -d+ -f1`
    hh=`echo $cropbox | tr "x" "+" | cut -d+ -f2`
    xo=`echo $cropbox | tr "x" "+" | cut -d+ -f3`
    yo=`echo $cropbox | tr "x" "+" | cut -d+ -f4`
    if [ $hh -gt 40 ]; then
    magick charts.png -alpha off -crop $cropbox +repage tmp.png
    std=`magick tmp.png -format "%[fx:standard_deviation]" info:`
    std_test=`magick xc: -format "%[fx:($std>0.1)?1:0]" info:`
    echo "$std; $std_test"
    echo ""
    if [ $std_test = 1 ]; then
    mv tmp.png charts_$i.png
    fi
    fi
    done
    

    Resulting Crops:

    enter image description here

    enter image description here

    enter image description here

    enter image description here

    enter image description here

    enter image description here

    enter image description here

    enter image description here

    enter image description here

    ADDITION

    Here is an alternate that get the full rows of regions.

    ht_thresh=40
    cropboxArr=(`im7 magick charts.png -set option:area "%[fx:w*$ht_thresh]" -alpha off 
    -threshold 1% 
    -scale 1x! 
    -threshold 25% -type bilevel 
    -define connected-components:area-threshold="%[area]" 
    -define connected-components:mean-color=true 
    -define connected-components:exclude-header=true 
    -define connected-components:verbose=true 
    -connected-components 8 cleaned.png | grep "gray(255)" | awk '{print $2}'`)
    num=${#cropboxArr[*]}
    echo $num
    for ((i=0; i<num; i++)); do
    cropbox=${cropboxArr[$i]}
    echo "$i; $cropbox;"
    ww=`echo $cropbox | tr "x" "+" | cut -d+ -f1`
    hh=`echo $cropbox | tr "x" "+" | cut -d+ -f2`
    xo=`echo $cropbox | tr "x" "+" | cut -d+ -f3`
    yo=`echo $cropbox | tr "x" "+" | cut -d+ -f4`
    if [ $hh -gt $ht_thresh ]; then
    im7 magick charts.png -alpha off -crop "%wx$hh+$xo+$yo" +repage -trim +repage tmp.png
    std=`im7 magick tmp.png -format "%[fx:standard_deviation]" info:`
    std_test=`im7 magick xc: -format "%[fx:($std>0.13)?1:0]" info:`
    echo "$std; $std_test"
    echo ""
    if [ $std_test = 1 ]; then
    mv tmp.png charts_$i.png
    fi
    fi
    done
    

    Results:

    enter image description here

    enter image description here

    enter image description here

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search