removing vertical lines in grayscale images by thickness

Suppose we have a grayscale image that contains vertical lines. Now suppose that not all vertical lines are the same, some of them have different thickness.

Question is, is there a way, in MATLAB or other programming language, to filter the vertical lines by thickness? In other words, based on the user selected thickness, only thin vertical lines are removed, while all other thick vertical lines are kept intact? or all thick vertical lines removed, all other thin vertical lines intact?

I tried experimenting with MATLAB with the morphological operators, but nothing seems to really work, perhaps I am using the wrong method.

edit: I am thinking about one possible way to solve this, but not in code, just as an idea. Every vertical line has 2 vertical edges. For every pair of 2 edges (1 line), calculate distance between two edges. Then, based on user threshold, remove or keep corresponding lines based on these calculated distances.

edit2: here is sample image: How to remove only 3 thin lines and keep 2 thick lines OR how to remove 2 thick lines and keep 3 thin lines? That is what I have to accomplish.

edit3: what I mean by remove is in this context interpolate them with the background (average value of left and right close pixels).

• Can you please post a sample image? – A_A May 19 '18 at 17:52
• It can be really any image, you can even make your own, something like this: mathworks.com/help/images/… , I was thinking about using Radon/Hough maybe, but not sure how to work with those. The point of my question is just in general, not about a specific image out there. Thank you – Dan May 19 '18 at 19:36
• Thank you, are the bars repetitive across the image? Is there content within the bars that you would be interested in preserving or are you simply interested in removing something like a banding artifact? – A_A May 19 '18 at 20:02
• I am not worried about overlapping bars, just separate bars of different thickness (length). They can repeat, but doesn't have to. The goal is to find a filtering method of only removing vertical bars based on some threshold (thickness length), so that you keep bars above threshold and remove below, or vice-versa. – Dan May 19 '18 at 20:52
• Dan, If you link to a sample image we could assist more easily. I have an idea which is based on some assumptions. I just need to see a sample to make sure the assumptions make sense. – Royi May 19 '18 at 22:38

Here is some Python code that does mostly what you want. You need to add the thick/thin line selection logic. You'll probably want to tweak it some more too. There seem to be some artifacts when it is turned back into a .jpg.

from PIL    import Image

import numpy as np

#============================================================
def main():

theImage = Image.open( "test.jpg")

theRangeMin = 0
theRangeMax = 7

#---- Get the Size

theSize = theImage.size

theWidth  = theSize[0]
theHeight = theSize[1]

print theWidth, theHeight

#---- Scan some Rows

theSum = np.zeros( theWidth )
theScanCount = 0

for n in range( 100, theHeight, 100 ):
print n
theScanCount += 1

for m in range( theWidth ):

#---- Calculate Derivatives

theFirst  = np.zeros( theWidth )
theSecond = np.zeros( theWidth )
theSum /= ( 3.0 * theScanCount )

for m in range( 1, theWidth-1 ):
theFirst[m]  = ( theSum[m+1] - theSum[m-1] ) * 0.5
theSecond[m] = theSum[m+1] - 2.0 * theSum[m] + theSum[m-1]
print m, theSum[m], theSecond[m], theFirst[m]

#---- Find Vertical Lines

theThreshold = 10

theLastFirst = 0

theLines = []

m = 1
while m < theWidth-1:
if theSum[m] < theThreshold:
while m < theWidth-1:
if theSum[m] > theThreshold and theFirst[m] < theThreshold:
break
m += 1

print theLastFirst, m
theRange = m - theLastFirst
theRate = 1.0 / float( theRange )
if theRangeMin <= theRange and theRange <= theRangeMax :
theLines.append( [ theLastFirst, m, theRange, theRate ] )

elif abs( theFirst[m] ) < theThreshold : theLastFirst = m

m += 1

#---- Hide the Lines

theLineCount = len( theLines )

for n in range( theHeight ):
for p in range( theLineCount ):
theLine = theLines[p]

theSpot = theLine[0]
theRange = theLine[2]
theRate  = theLine[3]
theSlope = ( theLastVal - theFirstVal ) * theRate

if n == 100: print theSpot, theFirstVal, theLastVal

for r in range( 1, theRange ):
theVal = theFirstVal + r * theSlope
theGrey = int( theVal ) & 0xFF
theShade = ( theGrey << 16 ) + ( theGrey << 8 ) + theGrey
if n == 100: print "     ", theVal

#---- Write the Image to a File

theImage.save( "out.jpg" )

#============================================================
main()



Followup:

Here is the debug output at the first thin line:

194 119.857142857 8.85714285714 -1.14285714286
195 123.142857143 -3.71428571429 1.42857142857
196 122.714285714 -64.7142857143 -32.7857142857
197 57.5714285714 13.8095238095 -58.2380952381
198 6.2380952381 88.0 -7.33333333333
199 42.9047619048 42.7142857143 58.0238095238
200 122.285714286 -79.2380952381 39.7619047619
201 122.428571429 0.428571428571 0.357142857143
202 123.0 -0.142857142857 0.5
203 123.428571429 0.0 0.428571428571
204 123.857142857 0.142857142857 0.5
205 124.428571429 -0.285714285714 0.428571428571
206 124.714285714 1.42108547152e-14 0.285714285714

• Interesting, I will try to convert this into MATLAB code. Can you please just explain the method in words. The strategy. Is this purely your code or you took from somewhere? Thank you! – Dan May 21 '18 at 22:55
• @Dan, This is freshly produced custom code in response to your question. I added some of the output so you can see how the numbers work. Installing Python might be easier than converting to MATLAB. I ended up not using the "second derivative" calculation, so you can slice that out. Basically the code looks for a low area (notice because of the jpg compressing, the stripes aren't that stark), then looks in each direction for "level" values. The comments and the variables names should allow you to follow the strategy. – Cedron Dawg May 22 '18 at 1:54
• I already tried it in Python, works nicely to hide the lines, very awesome job! Now only thing is from here, to allow hiding lines based on thickness level. Thank you – Dan May 22 '18 at 2:30
• @Dan,Your image wasn't exactly greyscale, as in R=G=B. You may get slightly better results if you ramp the R, G, and B values separately when removing the line. I did the quick and dirtly average since it was close to greyscale. If you like my answer, then you should accept it, maybe even upvote it. – Cedron Dawg May 22 '18 at 18:37
• OK, I accepted your answer, best for now :) Even tho the main problem is not solved yet. – Dan May 22 '18 at 19:49