@ALL This is an edit of the original question to bring a little more light on the subject.
Problem Statement
- Suppose there is an industrial P&ID plot.
- Aiming to color only some lines important to the process.
- The user should only click (left mouse-click) on the line segment to get it colored.
Problem Approach
I am new to programming -> using Python (3.5) to try this out. The way I see it the algorithm is like this:
- The plot will be in .pdf format. Therefore I could employ PIL ImageGrab or convert .pdf to .png as presented in this example
- The algorithm will search for pixels around the mouse click, then compare it to another portion of identical size (let's say a strip of 6x3 px), but one step to the left/right (be it 1-5 px)
- Checking the mean of their differences will tell us if the two strips are identical
- This way the algorithm should find both the line endings, arrows, corners or other elements
- Once this is found, the positions recorded and the markup line drawn, the user is expected to pick another line
Summed up
- Click on the wanted line
- Grab a small portion of the image around the mouse click
- Check if the line is either horizontal or vertical
- Crop an horizontal/vertical slice of a given size
- Find line endings and record the endings positions
- Between the two found positions draw a line of a certain color (let's say green)
- Wait for the next line to be picked and repeat
Other thoughts
- Attached you can find two pictures of a sample image and what I am trying to achieve.
- Tried to find "holes" in the slices using the approach found here: OpenCV to find line endings
- There is no strict rule in sticking with ImageGrab routine or anything alike
- If you know other tactics that I could use, please feel free to comment
- Any advice is welcome and sincerely appreciated
Sample image:
Desired Result (modified in Paint):
Adding an update to the post with the work I tried out so far
I've done some modifications on the original code so I will post it below. Everything in comments is either for debug or explanations. Your help is highly appreciated! Do not be afraid to intervene.
import win32gui as w
from PIL import ImageStat, ImageChops, Image, ImageDraw
import win32api as wa
img=Image.open("Trials.jpg")
img_width=img.size[0]
img_height=img.size[1]
#Using 1920 x 1080 resolution
#Hide the taskbar to center the Photo Viewer
#Defining a way to make sure the mouse click is inside the image
#Substract the width from total and divide by 2 to get base point of the crop
width_lim = (1920 - img_width)/2
height_lim = (1080 - img_height)/2-7
#After several tests, the math in calculating the height is off by 7 pixels, hence the correction
#Use these values when doing the crop
#Check if left mouse button was pressed and record its position
left_p = wa.GetKeyState(0x01)
#print(left_p)
while True :
a=wa.GetKeyState(0x01)
if a != left_p:
left_p = a
if a<0 :
pos = w.GetCursorPos()
pos_x=pos[0]-width_lim
pos_y=pos[1]-height_lim
# print(pos_x,pos_y)
else:
break
#img.show()
#print(img.size)
#Define the crop height; size is doubled
height_size = 10
#Define max length limit
#Getting a horizontal strip
im_hor = img.crop(box=[0, pos_y-height_size, img_width, pos_y+height_size])
#im_hor.show()
#failed in trying crop a small square of 3x3 size using the pos_x
#sq_size = 3
#st_sq = im_hor.crop(box=[pos_x,0,pos_x+sq_size,height_size*2])
#st_sq.show()
#going back to the code it works
#crop a standard strip and compare with a new test one
#if the mean of difference is zero, the strips are identical
#still looking for a way to find the position of the central pixel (that would be the one with maximum value - black)
strip_len = 3
step = 3
i = pos_x
st_sq = im_hor.crop(box=[i,0,i+strip_len,height_size*2])
test_sq = im_hor.crop(box=[i+step,0,i+strip_len+step,height_size*2])
diff = ImageChops.difference(st_sq,test_sq)
stat=ImageStat.Stat(diff)
mean = stat.mean
mean1 = stat.mean
#print(mean)
#iterate to the right until finding a different strip, record position
while mean==[0,0,0]:
i = i+1
st_sq = im_hor.crop(box=[i,0,i+strip_len,height_size*2])
#st_sq.show()
test_sq = im_hor.crop(box=[i+step,0,i+strip_len+step,height_size*2])
#test_sq.show()
diff = ImageChops.difference(st_sq,test_sq)
#diff.show()
stat=ImageStat.Stat(diff)
mean = stat.mean
# print(mean)
print(i-1)
r = i-1
#print("STOP")
#print(r)
#record the right end as r = i-1
#iterate to the left until finding a different strip. record the position
while mean1==[0,0,0]:
i = i-1
st_sq = im_hor.crop(box=[i,0,i+strip_len,height_size*2])
#st_sq.show()
test_sq = im_hor.crop(box=[i+step,0,i+strip_len+step,height_size*2])
#test_sq.show()
diff = ImageChops.difference(st_sq,test_sq)
#diff.show()
stat=ImageStat.Stat(diff)
mean1 = stat.mean
# print(mean)
#print("STOP")
print(i+1)
l = i+1
#record the left end as l=i+1
test_draw = ImageDraw.Draw(img)
test_draw.line([l,pos_y,r,pos_y], fill=128)
img.show()
#find another approach or die trying!!!
Below is the result I got. It is not what I was hoping for, but I feel like being on the right track. I could really use some help on finding the pixel position in a strip and make it relative to the big picture pixel position.
Another image of the sort, in better quality, but yet bringing more problems into the fray.