# How to find certain line pattern on image

**URL:** <https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691>\
**Category:** Uncategorized\
**Tags:** imgproc\
**Created:** [June 4, 2021, 3:24pm UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691 "2021-06-04T15:24:08Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![trojek](https://avatars.discourse-cdn.com/v4/letter/t/a88e4f/32.png) [@trojek](https://forum.opencv.org/u/trojek)\
**Post date:** [June 4, 2021, 3:24pm UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/1 "2021-06-04T15:24:08Z")

</div>

Hi.

I have a images like included but without red line.

My goal is to get the equation or list of points of a line (red on the image) which looks like in the below image:  
 ![threshold_close_line](https://us1.discourse-cdn.com/flex020/uploads/opencv/original/2X/e/eaedc3fd2cef39e33e82cc247860818ebe72fd86.jpeg)

There are at least patterns that can be detected:

1. The longest vertical white line
2. “Place” where horizontal line is split into two parts (left one is the lower and right one is upper on the image)

Does anyone have an idea how can I get the list of points (equation) of points selected by red color on the second image?

---

<div class="post-metadata">

**Author:** ![crackwitz](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.opencv.org/crackwitz/32/14_2.png) [@crackwitz](https://forum.opencv.org/u/crackwitz)\
**Post date:** [June 4, 2021, 3:37pm UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/2 "2021-06-04T15:37:21Z")

</div>

welcome.

how did that break happen? what’s the picture before thresholding?

---

<div class="post-metadata">

**Author:** ![trojek](https://avatars.discourse-cdn.com/v4/letter/t/a88e4f/32.png) [@trojek](https://forum.opencv.org/u/trojek)\
**Post date:** [June 4, 2021, 4:48pm UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/3 "2021-06-04T16:48:32Z")

</div>

The brake is in a place where laser light splits on wooden brick. In other words, the first part of the line is at a different height from the second part of the line.

Here is the image before the threshold:  
 ![base](https://us1.discourse-cdn.com/flex020/uploads/opencv/original/2X/d/de4d1c40c99620fbc9c813f111cb8767c87fcca3.jpeg)

What do you think about using “Hough Line Transform” algorithm?

---

<div class="post-metadata">

**Author:** ![crackwitz](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.opencv.org/crackwitz/32/14_2.png) [@crackwitz](https://forum.opencv.org/u/crackwitz)\
**Post date:** [June 4, 2021, 9:33pm UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/4 "2021-06-04T21:33:35Z")

</div>

that’s the context I needed to understand the situation. it looked like some kind of broken nail but now I know it’s not.

here’s a step towards a solution: a profile line, for every column it gives the y coordinate of mean brightness. it’s not perfect but neither is the picture 😉 you can run this on the thresholded picture as well. might even give you cleaner results.

you can do a derivative (`np.gradient`) on that profile and find extrema in its slope. those will be where the break/step is.

 ![image](https://us1.discourse-cdn.com/flex020/uploads/opencv/original/2X/d/df463c95cf83276a4c6b7e83b30d029a747e8212.png)

```python
import cv2 as cv
import matplotlib.pyplot as plt

im = cv.imread("de4d1c40c99620fbc9c813f111cb8767c87fcca3.jpeg", cv.IMREAD_GRAYSCALE)
h, w = im.shape
gy, gx = np.mgrid[0:h, 0:w]

# weighted sum
profile = np.sum(im * gy, axis=0) / np.sum(im, axis=0)

# negative to make it look like the picture
# I can't be bothered to mess with the axes so positive y goes down instead of up
plt.plot(-profile)

plt.show()

```

gradient:

 ![image](https://us1.discourse-cdn.com/flex020/uploads/opencv/original/2X/3/36e2244c5ff94163e7ae30e429b2cd24c8d8545a.png)

---

<div class="post-metadata">

**Author:** ![trojek](https://avatars.discourse-cdn.com/v4/letter/t/a88e4f/32.png) [@trojek](https://forum.opencv.org/u/trojek)\
**Post date:** [June 5, 2021, 3:56pm UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/5 "2021-06-05T15:56:58Z")

</div>

Thank you @crackwitz. Your solution works very well.  
By the way, I got better results on the thresholded image.

How can I get the value of x for the smallest y? I tried np.min(gradient, axis=0), but I got nan. When I print gradient all values are nan.

In the above example, we assume that the line is horizontal. I understand that if the line will be vertical I need to calculate the x coordinate of mean brightness.  
How can I deal with a line which diagonal (or has an unknown angle)?

How to deal with multiple lines in different directions?

---

<div class="post-metadata">

**Author:** ![crackwitz](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.opencv.org/crackwitz/32/14_2.png) [@crackwitz](https://forum.opencv.org/u/crackwitz)\
**Post date:** [June 5, 2021, 6:11pm UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/6 "2021-06-05T18:11:51Z")

</div>

> [@trojek](#):
>
> I tried np.min(gradient, axis=0), but I got nan. When I print gradient all values are nan.

you can guess what I’ll ask of you next: to present precisely what you did. there’s no way to help you without that information. you should anticipate this.

I’d also recommend `argmin`, which gives the _index_ of the smallest element.

do you actually _have to_ handle directions other than horizontal? instead you could tell the user to take the picture in the right orientation. there are ways to estimate orientation but there’s insufficient information to pick one yet.

you should present the _entire_ problem _thoroughly_. playing 20 questions and guessing what you need is counter-productive.

---

<div class="post-metadata">

**Author:** ![trojek](https://avatars.discourse-cdn.com/v4/letter/t/a88e4f/32.png) [@trojek](https://forum.opencv.org/u/trojek)\
**Post date:** [June 5, 2021, 6:32pm UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/7 "2021-06-05T18:32:53Z")

</div>

My whole code looks like this:

```python
import cv2
import matplotlib.pyplot as plt
import numpy as np

orginal_img = cv2.imread("base_file.bmp", cv2.IMREAD_GRAYSCALE)

ret, threshold_img = cv2.threshold(orginal_img, 41, 255, cv2.THRESH_BINARY)
print("threshold: ", threshold_img)

h, w = threshold_img.shape
gy, _ = np.mgrid[0:h, 0:w]

profile = np.sum(threshold_img * gy, axis=0) / np.sum(threshold_img, axis=0)
print("profile: ", profile)

gradient = np.gradient(profile)
print("gradient: ", gradient)

min_value = np.min(gradient, axis=0)
print("min_value: ", min_value)

plt.plot(gradient)
plt.show()

```

In console one can see:

```auto
threshold: [[0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]
 ...
 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]]
main.py:17: RuntimeWarning: invalid value encountered in true_divide
  profile = np.sum(threshold_img * gy, axis=0) / np.sum(threshold_img, axis=0)
profile: [nan nan nan ... nan nan nan]
gradient: [nan nan nan ... nan nan nan]
min_value: nan

```

I guess that I divide by 0 that’s why I get “nan”.  
How to get the value of y where x is minimum:

 ![gradient](https://us1.discourse-cdn.com/flex020/uploads/opencv/original/2X/1/19ef37989587159c2f57f0cd18ae676d5c4a61d9.png)

For example from the above image, I want to get a value near 2150.

> [@crackwitz](#):
>
> do you actually _have to_ handle directions other than horizontal? instead you could tell the user to take the picture in the right orientation. there are ways to estimate orientation but there’s insufficient information to pick one yet.
> 
> you should present the _entire_ problem _thoroughly_ . playing 20 questions and guessing what you need is counter-productive.

You are right. I need to prepare more materials (images) to present the entire problem. I will do it shortly.

---

<div class="post-metadata">

**Author:** ![crackwitz](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.opencv.org/crackwitz/32/14_2.png) [@crackwitz](https://forum.opencv.org/u/crackwitz)\
**Post date:** [June 5, 2021, 7:10pm UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/8 "2021-06-05T19:10:07Z")

</div>

```python
#!/usr/bin/env python3
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt

original_img = cv.imread("base_file.bmp", cv.IMREAD_GRAYSCALE)
cv.namedWindow("original_img", cv.WINDOW_NORMAL)
cv.imshow("original_img", original_img)

reit, threshold_img = cv.threshold(original_img, 41, 255, cv.THRESH_BINARY)
print("threshold: ", threshold_img)
cv.namedWindow("threshold_img", cv.WINDOW_NORMAL)
cv.imshow("threshold_img", threshold_img)
cv.waitKey(1)

h, w = threshold_img.shape
gy, _ = np.mgrid[0:h, 0:w]

profilesum = np.sum(threshold_img, axis=0)

# weighted sum, calculates the "center of mass" for every column
profile = np.sum(threshold_img * gy, axis=0) / profilesum

# alternative: find first and last white pixel of every column, calculate midpoint

# let's only consider columns that have at least 10 pixels set
# less causes some noise
# OpenCV uses 0 and 255 for thresholded/binary pictures
# I need 0/1 for this step:
boolean_img = (threshold_img > 0)
profilevalid = (np.sum(boolean_img, axis=0) >= 10)

# nans aren't plotted
# invalidate columns that have less than 10 pixels set
profile[~profilevalid] = np.nan

gradient = np.gradient(profile)
#print("gradient: ", gradient)

extremum_value = np.nanmin(gradient, axis=0) # nanmin: min but ignore nans
print("extremum_value: ", extremum_value)

extremum_x = np.nanargmin(gradient)
print("extremum_x: ", extremum_x)

fig, axs = plt.subplots(2, sharex=True)
fig.set_size_inches(12, 6)
axs[0].axvline(x=extremum_x, color='r', dashes=(1,2))
axs[0].plot(profile)
axs[1].plot([extremum_x], [extremum_value], 'ro')
axs[1].plot(gradient)
fig.tight_layout()
plt.show()
cv.destroyAllWindows()

```

 ![image](https://us1.discourse-cdn.com/flex020/uploads/opencv/original/2X/c/c8cb7121df606a0d51d7de8bc8ca326b5e9d81d9.png)

---

<div class="post-metadata">

**Author:** ![trojek](https://avatars.discourse-cdn.com/v4/letter/t/a88e4f/32.png) [@trojek](https://forum.opencv.org/u/trojek)\
**Post date:** [June 6, 2021, 2:35pm UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/9 "2021-06-06T14:35:48Z")

</div>

Thank you for your detailed answer.  
Before getting materials for an explanation of the entire problem I would like to ask one thing.

What exactly does this line?

> profile = np.sum(threshold\_img \* gy, axis=0) / profilesum

Can one interpret it as the code below?

> for i in range(0, len(threshold\_img)):  
> for j in range(0, len(threshold\_img[i])):  
> profile[i][j] = sum(threshold\_img[i][j] \* gy[i][j] / profilesum[j])

I make a simple image like this one  
 ![example](https://us1.discourse-cdn.com/flex020/uploads/opencv/original/2X/4/4c86e2aa1a190301a5547a4f50b97b05feaf9e31.jpeg)

I examine what does profile variable returns, I “feel” that this is what I need but I don’t understand how it is created and why we use gx (which is [[0, 0, 0, …], [1, 1, 1, …] …]). Could you please elaborate on this topic or send me a link to material from which I can get these pieces of information?

---

<div class="post-metadata">

**Author:** ![crackwitz](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.opencv.org/crackwitz/32/14_2.png) [@crackwitz](https://forum.opencv.org/u/crackwitz)\
**Post date:** [June 6, 2021, 3:44pm UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/10 "2021-06-06T15:44:17Z")

</div>

> [@trojek](#):
>
> Can one interpret it as the code below?

no. profile is a 1-dimensional array. your interpretation accesses it like a 2-dimensional array.

I’ll explain my code:

```python
profilesum = np.sum(threshold_img, axis=0)

# weighted sum, calculates the "center of mass" for every column
profile = np.sum(threshold_img * gy, axis=0) / profilesum

```

we are calculating a weighted sum or weighted average. the goal is to figure out the “center of mass” of the pixels for every column.

threshold\_img is the image.

np.sum along axis 0 means every column is collapsed into the sum of its values. you get a row vector that contains something proportional to the _number of pixels_ in every column of the image. it would be exactly the number of pixels if the pixel values were 0 or 1, but they’re 0 and 255, so you get a weight of 255 in almost everything. I’m trying to explain this as simply as possible, so I will only explain this for binary images.

gy merely gives us the y-coordinate for every pixel. gx is not used. I only gave it a name because it is part of the data returned from np.mgrid

`threshold_img * gy` leaves in every pixel either that coordinate or 0.

if we want the average y coordinate of those pixels, we have to divide the _sum_ by the _number_ of pixels. that’s what the whole expression does.

you have already proposed a small test picture to investigate. now _look_ at the values inside of all the arrays.

---

<div class="post-metadata">

**Author:** ![trojek](https://avatars.discourse-cdn.com/v4/letter/t/a88e4f/32.png) [@trojek](https://forum.opencv.org/u/trojek)\
**Post date:** [June 7, 2021, 10:10am UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/11 "2021-06-07T10:10:17Z")

</div>

Now everything is clear for me, thank you!

Going back to the problem (which isn’t very well defined as I play with OpenCV rather when solving the real problem).

Let assume that on every image I have only one line, the line can be only vertical or horizontal, the crack on the line can be either perpendicular or at an angle (different than 90 degrees which I understand as perpendicular). Resuming we can have 4 situations:

1. horizontal line, perpendicular crack (as in the image that we already discussed)
2. horizontal line, crack at angle 0-180
3. vertical line, perpendicular crack (as in the image that we already discussed)
4. vertical line, crack at angle 0-180

In the case of the vertical and horizontal lines, one should create a “profile” base on rows instead of columns. We can reduce the problem to two cases: perpendicular crack and crack at angle 0-180, the first one is well examined.

How to get the angle of crack and “point of crack” if it isn’t perpendicular to the line?

I attached the image horizontal line with crack at angle about 45 degrees.

 ![diagonal](https://us1.discourse-cdn.com/flex020/uploads/opencv/original/2X/d/d734156839cc44370e5457dec749f4ec7fa657bd.jpeg)

---

<div class="post-metadata">

**Author:** ![crackwitz](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.opencv.org/crackwitz/32/14_2.png) [@crackwitz](https://forum.opencv.org/u/crackwitz)\
**Post date:** [June 7, 2021, 10:35am UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/12 "2021-06-07T10:35:44Z")

</div>

too little data to estimate the angle of the break. I see you have full resolution data. _do_ post that instead of low resolution versions.

next you might tell me that the laser line can move across the scene? I hate to guess.

why is the angle of the break even interesting? don’t say that someone just wants to know. that’s no motivation.

I expected an explanation that includes perhaps a picture of the scene that is _not_ in darkness (and a picture that is _not_ from the camera that does the “measuring”, i.e. a view from the outside), or a claim that the camera is fixed (relative to the scene) and the laser line moves. I’m looking through a keyhole here. it’s frustrating.

you need more data. and I can’t speculate on how to get it because I’m literally tapping in the dark.

---

<div class="post-metadata">

**Author:** ![trojek](https://avatars.discourse-cdn.com/v4/letter/t/a88e4f/32.png) [@trojek](https://forum.opencv.org/u/trojek)\
**Post date:** [June 7, 2021, 12:36pm UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/13 "2021-06-07T12:36:07Z")

</div>

> [@crackwitz](#):
>
> too little data to estimate the angle of the break. I see you have full resolution data. _do_ post that instead of low resolution versions.

What I send is just a crop from the whole image, there is no more data. Rest of the image almost black.

> [@crackwitz](#):
>
> next you might tell me that the laser line can move across the scene? I hate to guess.

Theoretically, it can move but during taking the picture it stays in one place. Why it is important?

> [@crackwitz](#):
>
> why is the angle of the break even interesting? don’t say that someone just wants to know. that’s no motivation.

I try to know how much information about the position of a flat object (plywood rectangle) I can get only from image analysis. Depends on the result I will use it in my project in which I use laser light to mark positions on a stage or not.

> [@crackwitz](#):
>
> I expected an explanation that includes perhaps a picture of the scene that is _not_ in darkness (and a picture that is _not_ from the camera that does the “measuring”, i.e. a view from the outside), or a claim that the camera is fixed (relative to the scene) and the laser line moves. I’m looking through a keyhole here. it’s frustrating.

The camera is fixed relative to the scene, the laser lines are also fixed (but can be moved, but we can assume that there are horizontal and vertical lines). I send a picture on Wednesday.

---

<div class="post-metadata">

**Author:** ![crackwitz](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.opencv.org/crackwitz/32/14_2.png) [@crackwitz](https://forum.opencv.org/u/crackwitz)\
**Post date:** [June 7, 2021, 4:05pm UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/14 "2021-06-07T16:05:13Z")

</div>

based on the latest picture, I guess an _angle_ could be estimated somehow, very approximately. the laser line is very narrow. there isn’t much to work with. I’m gonna leave it at that. I’ll be more reserved from now on.

---

<div class="post-metadata">

**Author:** ![trojek](https://avatars.discourse-cdn.com/v4/letter/t/a88e4f/32.png) [@trojek](https://forum.opencv.org/u/trojek)\
**Post date:** [June 9, 2021, 2:48pm UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/15 "2021-06-09T14:48:18Z")

</div>

Hi @crackwitz. I don’t understand what I did wrong that you will be “more reserved from now”. I had no bad intentions.

---

<div class="post-metadata">

**Author:** ![crackwitz](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.opencv.org/crackwitz/32/14_2.png) [@crackwitz](https://forum.opencv.org/u/crackwitz)\
**Post date:** [June 9, 2021, 4:44pm UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/16 "2021-06-09T16:44:26Z")

</div>

there is very little data in that picture and I think it’s not enough to answer questions like “what’s the orientation of the break”. I think it’s somewhat doable but the result will be guesswork and I suspect the task is made needlessly difficult by the poor picture quality and your other stated constraints, for which there was no justification given.

if you want to test the limits of feasibility, that’s a challenge you give yourself.

---

<div class="post-metadata">

**Author:** ![trojek](https://avatars.discourse-cdn.com/v4/letter/t/a88e4f/32.png) [@trojek](https://forum.opencv.org/u/trojek)\
**Post date:** [July 3, 2021, 4:19pm UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/17 "2021-07-03T16:19:18Z")

</div>

Hi @crackwitz,

I have one more question related to this topic. In some situations “split line” looks like this:

![example](https://us1.discourse-cdn.com/flex020/uploads/opencv/original/2X/e/e85d3e78db213fd2b6ca438198d11bd48dc6d492.jpeg)

I want to find the beginning and the end of the “split line”.

The profile of of this line (with selected searched points) looks like this:

![profile_selected_points](https://us1.discourse-cdn.com/flex020/uploads/opencv/original/2X/5/539dae47cdf9acddd0334af42537dc65d791fce3.png)

I tried to make this line smoother (by rolling average) and then make a gradient from it, but after smoothing the line the point in which the function’s value increase changes its position on the x value.

Do you have any idea how to get those points?

---

<div class="post-metadata">

**Author:** ![crackwitz](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.opencv.org/crackwitz/32/14_2.png) [@crackwitz](https://forum.opencv.org/u/crackwitz)\
**Post date:** [July 3, 2021, 5:49pm UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/18 "2021-07-03T17:49:42Z")

</div>

second derivatives.

signal looks like

```
           _____
          /
         /
        /
       /
      /
_____ /

```

first derivative looks like

```
     _____
 ____/ \_____

```

second derivative looks like

```
____/\____  ____
          \/

```

if you do the moving average wrong, you introduce a phase shift. you just add/subtract that then.

I’d recommend a gaussian filter. spares you from understanding the details.

[https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.gaussian\_filter.html](https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.gaussian_filter.html)

---

<div class="post-metadata">

**Author:** ![trojek](https://avatars.discourse-cdn.com/v4/letter/t/a88e4f/32.png) [@trojek](https://forum.opencv.org/u/trojek)\
**Post date:** [July 4, 2021, 8:55am UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/19 "2021-07-04T08:55:41Z")

</div>

Unfortunately, I can’t get a satisfying result.

When I don’t smooth the function I can’t get any interesting data from derivatives. The plot looks like this:

![gradient](https://us1.discourse-cdn.com/flex020/uploads/opencv/original/2X/9/9edfd821d1ee348dafb857b21869fc37771afd79.png)

I tried to use botch convolved and gausian filters and I got result as below (gradient from profile of lined on which filters were used):  
 ![gradient_convolved](https://us1.discourse-cdn.com/flex020/uploads/opencv/original/2X/0/064b21c2d8b0e2e6f5d7a777072eb92f40cad243.png)  
 ![gradient_gaussian](https://us1.discourse-cdn.com/flex020/uploads/opencv/original/2X/b/b4592696739520693155d04c9e615bafeb47b453.png)

Theoretically, I can try to add/subtract the phase shift, but I don’t know how to calculate the shift.

Could you please provide me more hints?

---

<div class="post-metadata">

**Author:** ![crackwitz](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.opencv.org/crackwitz/32/14_2.png) [@crackwitz](https://forum.opencv.org/u/crackwitz)\
**Post date:** [July 4, 2021, 12:18pm UTC](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691/20 "2021-07-04T12:18:23Z")

</div>

vary the smoothing (try less), and you still need a second derivative

[Next page](https://forum.opencv.org/t/how-to-find-certain-line-pattern-on-image/3691.md?page=2)
