OpenCV line detection for 45 degree lines












4















I have an image:



original image where opencv houghP transform can't find the -45 degree line



In this image, the OpenCV Hough transform can't detect the big -45 degree line using



minLineLength = 150 
maxLineGap = 5
line_thr = 150
linesP = cv.HoughLinesP(dst, 1, np.pi / 180, line_thr, None, minLineLength, maxLineGap)


The only lines found are:



lines found



I tried playing with various thresholds also but I can't find the line here.



If I manually crop the image like this:



cropped version where line is found



then I can clearly see the OpenCV Hough transform finding the right line:



line found



I want to find this same line in the non cropped version. Any suggestions on the non-cropped version to find it?



Also there can be cases where there is no line at all or the line doesn't go all the way for X-axis length.
Examples



enter image description hereenter image description here










share|improve this question

























  • If you know the line is at 45 degrees, why use the Hough transform? You can keep it simple and look only for lines at 45 degrees.

    – Cris Luengo
    Nov 27 '18 at 7:34











  • Cris Luengo, any idea how that can be achieved, some code would be appreciated. I'm looking for length of line to greater than some units and in between some noise(black dots) can be there.

    – pilogo
    Nov 27 '18 at 10:05






  • 1





    you could use a filter and define a kernel which will smooth out anything that is not 45deg - oriented. This could be done with Kirsch operator or Sobel operator. There are most probably quite a few ways to achieve that. Check with stackoverflow.com/questions/8547127/…

    – Sane_or_not
    Nov 27 '18 at 10:05


















4















I have an image:



original image where opencv houghP transform can't find the -45 degree line



In this image, the OpenCV Hough transform can't detect the big -45 degree line using



minLineLength = 150 
maxLineGap = 5
line_thr = 150
linesP = cv.HoughLinesP(dst, 1, np.pi / 180, line_thr, None, minLineLength, maxLineGap)


The only lines found are:



lines found



I tried playing with various thresholds also but I can't find the line here.



If I manually crop the image like this:



cropped version where line is found



then I can clearly see the OpenCV Hough transform finding the right line:



line found



I want to find this same line in the non cropped version. Any suggestions on the non-cropped version to find it?



Also there can be cases where there is no line at all or the line doesn't go all the way for X-axis length.
Examples



enter image description hereenter image description here










share|improve this question

























  • If you know the line is at 45 degrees, why use the Hough transform? You can keep it simple and look only for lines at 45 degrees.

    – Cris Luengo
    Nov 27 '18 at 7:34











  • Cris Luengo, any idea how that can be achieved, some code would be appreciated. I'm looking for length of line to greater than some units and in between some noise(black dots) can be there.

    – pilogo
    Nov 27 '18 at 10:05






  • 1





    you could use a filter and define a kernel which will smooth out anything that is not 45deg - oriented. This could be done with Kirsch operator or Sobel operator. There are most probably quite a few ways to achieve that. Check with stackoverflow.com/questions/8547127/…

    – Sane_or_not
    Nov 27 '18 at 10:05
















4












4








4


1






I have an image:



original image where opencv houghP transform can't find the -45 degree line



In this image, the OpenCV Hough transform can't detect the big -45 degree line using



minLineLength = 150 
maxLineGap = 5
line_thr = 150
linesP = cv.HoughLinesP(dst, 1, np.pi / 180, line_thr, None, minLineLength, maxLineGap)


The only lines found are:



lines found



I tried playing with various thresholds also but I can't find the line here.



If I manually crop the image like this:



cropped version where line is found



then I can clearly see the OpenCV Hough transform finding the right line:



line found



I want to find this same line in the non cropped version. Any suggestions on the non-cropped version to find it?



Also there can be cases where there is no line at all or the line doesn't go all the way for X-axis length.
Examples



enter image description hereenter image description here










share|improve this question
















I have an image:



original image where opencv houghP transform can't find the -45 degree line



In this image, the OpenCV Hough transform can't detect the big -45 degree line using



minLineLength = 150 
maxLineGap = 5
line_thr = 150
linesP = cv.HoughLinesP(dst, 1, np.pi / 180, line_thr, None, minLineLength, maxLineGap)


The only lines found are:



lines found



I tried playing with various thresholds also but I can't find the line here.



If I manually crop the image like this:



cropped version where line is found



then I can clearly see the OpenCV Hough transform finding the right line:



line found



I want to find this same line in the non cropped version. Any suggestions on the non-cropped version to find it?



Also there can be cases where there is no line at all or the line doesn't go all the way for X-axis length.
Examples



enter image description hereenter image description here







image opencv image-processing opencv3.0 hough-transform






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 28 '18 at 12:33







pilogo

















asked Nov 27 '18 at 6:09









pilogopilogo

357




357













  • If you know the line is at 45 degrees, why use the Hough transform? You can keep it simple and look only for lines at 45 degrees.

    – Cris Luengo
    Nov 27 '18 at 7:34











  • Cris Luengo, any idea how that can be achieved, some code would be appreciated. I'm looking for length of line to greater than some units and in between some noise(black dots) can be there.

    – pilogo
    Nov 27 '18 at 10:05






  • 1





    you could use a filter and define a kernel which will smooth out anything that is not 45deg - oriented. This could be done with Kirsch operator or Sobel operator. There are most probably quite a few ways to achieve that. Check with stackoverflow.com/questions/8547127/…

    – Sane_or_not
    Nov 27 '18 at 10:05





















  • If you know the line is at 45 degrees, why use the Hough transform? You can keep it simple and look only for lines at 45 degrees.

    – Cris Luengo
    Nov 27 '18 at 7:34











  • Cris Luengo, any idea how that can be achieved, some code would be appreciated. I'm looking for length of line to greater than some units and in between some noise(black dots) can be there.

    – pilogo
    Nov 27 '18 at 10:05






  • 1





    you could use a filter and define a kernel which will smooth out anything that is not 45deg - oriented. This could be done with Kirsch operator or Sobel operator. There are most probably quite a few ways to achieve that. Check with stackoverflow.com/questions/8547127/…

    – Sane_or_not
    Nov 27 '18 at 10:05



















If you know the line is at 45 degrees, why use the Hough transform? You can keep it simple and look only for lines at 45 degrees.

– Cris Luengo
Nov 27 '18 at 7:34





If you know the line is at 45 degrees, why use the Hough transform? You can keep it simple and look only for lines at 45 degrees.

– Cris Luengo
Nov 27 '18 at 7:34













Cris Luengo, any idea how that can be achieved, some code would be appreciated. I'm looking for length of line to greater than some units and in between some noise(black dots) can be there.

– pilogo
Nov 27 '18 at 10:05





Cris Luengo, any idea how that can be achieved, some code would be appreciated. I'm looking for length of line to greater than some units and in between some noise(black dots) can be there.

– pilogo
Nov 27 '18 at 10:05




1




1





you could use a filter and define a kernel which will smooth out anything that is not 45deg - oriented. This could be done with Kirsch operator or Sobel operator. There are most probably quite a few ways to achieve that. Check with stackoverflow.com/questions/8547127/…

– Sane_or_not
Nov 27 '18 at 10:05







you could use a filter and define a kernel which will smooth out anything that is not 45deg - oriented. This could be done with Kirsch operator or Sobel operator. There are most probably quite a few ways to achieve that. Check with stackoverflow.com/questions/8547127/…

– Sane_or_not
Nov 27 '18 at 10:05














4 Answers
4






active

oldest

votes


















1














I implemented a slightly simpler algorithm than my other answer but in Python with OpenCV this time.



Basically, rather than taking the mean of vertical columns of pixels, it sums the pixels in the columns and chooses the column that is brightest. If I show the padded, rotated image with another image below representing the sums of the columns, you should see how it works:



enter image description here



#!/usr/bin/env python3

import cv2
import numpy as np

# Load image as greyscale
im = cv2.imread('45.jpg',cv2.IMREAD_GRAYSCALE)

# Pad with border so it isn't cropped when rotated
bw=300
bordered = cv2.copyMakeBorder(im, top=bw, bottom=bw, left=bw, right=bw, borderType= cv2.BORDER_CONSTANT)

# Rotate -45 degrees
w, h = bordered.shape
M = cv2.getRotationMatrix2D((h/2,w/2),-45,1)
paddedrotated = cv2.warpAffine(bordered,M,(h,w))
# DEBUG cv2.imwrite('1.tif',paddedrotated)

# Sum the elements of each column and find column with most white pixels
colsum = np.sum(paddedrotated,axis=0,dtype=np.float)
col = np.argmax(colsum)
# DEBUG cv2.imwrite('2.tif',colsum)

# Fill with black except for the line we have located which we make white
paddedrotated[:,:] = 0
paddedrotated[:,col] = 255

# Rotate back to straight
w, h = paddedrotated.shape
M = cv2.getRotationMatrix2D((h/2,w/2),45,1)
straight = cv2.warpAffine(paddedrotated,M,(h,w))

# Remove padding and save to disk
straight = straight[bw:-bw,bw:-bw]
cv2.imwrite('result.png',straight)


Note that you don't actually have to rotate the image back to straight and crop it back to its original size. You could actually stop after the first line that says:



col = np.argmax(colsum)


and use some elementary trigonometry to work out what that means in your original image.



Here is the output:



enter image description here





Keywords: line detection, detect line, rotate, pad, border, projection, project, image, image processing, Python, OpenCV, affine, Hough






share|improve this answer
























  • Awesome approach Mark, but a more general solution would be helpful because I can have images where there is no line at all(only some white dots), or lines which don't go all along the x axis. Added few more images in the original question.

    – pilogo
    Nov 28 '18 at 12:34











  • My other answer works fine for the new images. This answer also works fine for the new image with the partial line. If you don't want it to find anything in the image with no lines, you would need to think a minimum length threshold and apply it where the code says col = np.argmax(colsum)

    – Mark Setchell
    Nov 28 '18 at 12:53



















1














I did this on the command-line in Terminal with ImageMagick but you can apply exactly the same technique with OpenCV.



Step 1



Take the image and rotate it 45 degrees introducing black pixels as background where required:



convert 45.jpg -background black -rotate 45 result.png


enter image description here



Step 2



Now, building on the previous command, set every pixel to the median of the box 1px wide and 250px tall centred on it:



convert 45.jpg -background black -rotate 45 -statistic median 1x250 result.png


enter image description here



Step 3



Now, again building on the previous command, rotate it back 45 degrees:



convert 45.jpg -background black -rotate 45 -statistic median 1x250 -rotate -45 result.png


enter image description here





So, in summary, the entire processing is:



convert input.jpg -background black -rotate 45 -statistic median 1x250 -rotate -45 result.png


Obviously then crop it back to the original size and append side-by-side with the original for checking:



convert 45.jpg -background black -rotate 45 -statistic median 5x250 -rotate -45 +repage -gravity center -crop 184x866+0+0 result.png
convert 45.jpg result.png +append result.png


enter image description here





You can also use mean statistic plus thresholding rather than median since it is quicker than sorting to find the median, however it tends to lead to smearing:



convert 45.jpg -background black -rotate 45 -statistic mean 1x250 result.png


enter image description here



Your newly-added image gets processed to this result:



enter image description here






share|improve this answer

































    1














    The problem is clearly that the line you are searching for is not a line. It looks actually like a train of connected circles and boxes. Therefore, I recommend that you do the following:



    Find all contours in the image using find contours



    img = cv.imread('image.jpg')
    img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    ret, thresh = cv.threshold(img_gray, 127, 255, 0)
    img2, contours, hierarchy = cv.findContours(thresh, CHAIN_APPROX_SIMPLE ,cv.RETR_EXTERNAL)


    This will return many many contours, so use a loop to save only long enough contours. Since the image size is 814x1041 pixels, I assume the contour long if it is at least 10% of the image width which is almost 100 (you must apparently optimize this value)



    long_contours = 
    for contour in contours[i]:
    perimeter = cv2.arcLength(contour,True)
    if (perimeter > 0.1 * 1018) # 10% of the image width
    long_contours.append(contour)


    Now draw a rotated bounding rectangle around those long contours that might be a line as well. The long contour is considered a line if its width is much longer than its height, or its aspect ratio is large (such as 8, and you need also to optimize this value)



    for long_contour in long_contours:
    rect = cv2.minAreaRect(long_contour)
    aspec_ratio = rect.width / rect.height

    if aspec_ratio > 8 :
    box = cv2.boxPoints(rect)
    box = np.int0(box)
    cv2.drawContours(img,[box],0,(255,255,255),cv.FILLED)


    Finally you should get something like that. Please note the code here is for guidance only.



    Rotated rectangle over a suspected line






    share|improve this answer
























    • This is a good observation. I wish you hadn't used contours. That just makes no sense to me. You can do this much simpler and efficiently using connected component analysis. Contours are totally overused. You should only extract contours if you specifically need the contours of objects.

      – Cris Luengo
      Nov 28 '18 at 17:30











    • Thanks for the hint. I agree that using connected components could be easier and more efficient. There no specific advantage for using contours, except that I reuse them in many projects. I will change that in the future.

      – Sameh Yassin
      Nov 29 '18 at 8:13





















    1














    Your original code is fine as a whistle. The only problem is that your image contains too many information which mess up the accumulator scores. Everything will work out if you increase the line threshold to 255.



    minLineLength = 150 
    maxLineGap = 5
    line_thr = 255
    linesP = cv2.HoughLinesP(dst, 1, np.pi / 180.0, line_thr, None, minLineLength, maxLineGap)


    Here are the results using that value.
    3 lines are detected here due to the large white pixel size.



    [  1  41 286 326]
    [ 0 42 208 250]
    [ 1 42 286 327]


    enter image description here



    5 lines are detected around the same area due to the same reason as above. Reducing the pixel sizes using morphological operation or distance transform should fix this.



    [110 392 121 598]
    [112 393 119 544]
    [141 567 147 416]
    [ 29 263 29 112]
    [ 0 93 179 272]


    enter image description here



    No line found here.
    enter image description here






    share|improve this answer























      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53493709%2fopencv-line-detection-for-45-degree-lines%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      4 Answers
      4






      active

      oldest

      votes








      4 Answers
      4






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      1














      I implemented a slightly simpler algorithm than my other answer but in Python with OpenCV this time.



      Basically, rather than taking the mean of vertical columns of pixels, it sums the pixels in the columns and chooses the column that is brightest. If I show the padded, rotated image with another image below representing the sums of the columns, you should see how it works:



      enter image description here



      #!/usr/bin/env python3

      import cv2
      import numpy as np

      # Load image as greyscale
      im = cv2.imread('45.jpg',cv2.IMREAD_GRAYSCALE)

      # Pad with border so it isn't cropped when rotated
      bw=300
      bordered = cv2.copyMakeBorder(im, top=bw, bottom=bw, left=bw, right=bw, borderType= cv2.BORDER_CONSTANT)

      # Rotate -45 degrees
      w, h = bordered.shape
      M = cv2.getRotationMatrix2D((h/2,w/2),-45,1)
      paddedrotated = cv2.warpAffine(bordered,M,(h,w))
      # DEBUG cv2.imwrite('1.tif',paddedrotated)

      # Sum the elements of each column and find column with most white pixels
      colsum = np.sum(paddedrotated,axis=0,dtype=np.float)
      col = np.argmax(colsum)
      # DEBUG cv2.imwrite('2.tif',colsum)

      # Fill with black except for the line we have located which we make white
      paddedrotated[:,:] = 0
      paddedrotated[:,col] = 255

      # Rotate back to straight
      w, h = paddedrotated.shape
      M = cv2.getRotationMatrix2D((h/2,w/2),45,1)
      straight = cv2.warpAffine(paddedrotated,M,(h,w))

      # Remove padding and save to disk
      straight = straight[bw:-bw,bw:-bw]
      cv2.imwrite('result.png',straight)


      Note that you don't actually have to rotate the image back to straight and crop it back to its original size. You could actually stop after the first line that says:



      col = np.argmax(colsum)


      and use some elementary trigonometry to work out what that means in your original image.



      Here is the output:



      enter image description here





      Keywords: line detection, detect line, rotate, pad, border, projection, project, image, image processing, Python, OpenCV, affine, Hough






      share|improve this answer
























      • Awesome approach Mark, but a more general solution would be helpful because I can have images where there is no line at all(only some white dots), or lines which don't go all along the x axis. Added few more images in the original question.

        – pilogo
        Nov 28 '18 at 12:34











      • My other answer works fine for the new images. This answer also works fine for the new image with the partial line. If you don't want it to find anything in the image with no lines, you would need to think a minimum length threshold and apply it where the code says col = np.argmax(colsum)

        – Mark Setchell
        Nov 28 '18 at 12:53
















      1














      I implemented a slightly simpler algorithm than my other answer but in Python with OpenCV this time.



      Basically, rather than taking the mean of vertical columns of pixels, it sums the pixels in the columns and chooses the column that is brightest. If I show the padded, rotated image with another image below representing the sums of the columns, you should see how it works:



      enter image description here



      #!/usr/bin/env python3

      import cv2
      import numpy as np

      # Load image as greyscale
      im = cv2.imread('45.jpg',cv2.IMREAD_GRAYSCALE)

      # Pad with border so it isn't cropped when rotated
      bw=300
      bordered = cv2.copyMakeBorder(im, top=bw, bottom=bw, left=bw, right=bw, borderType= cv2.BORDER_CONSTANT)

      # Rotate -45 degrees
      w, h = bordered.shape
      M = cv2.getRotationMatrix2D((h/2,w/2),-45,1)
      paddedrotated = cv2.warpAffine(bordered,M,(h,w))
      # DEBUG cv2.imwrite('1.tif',paddedrotated)

      # Sum the elements of each column and find column with most white pixels
      colsum = np.sum(paddedrotated,axis=0,dtype=np.float)
      col = np.argmax(colsum)
      # DEBUG cv2.imwrite('2.tif',colsum)

      # Fill with black except for the line we have located which we make white
      paddedrotated[:,:] = 0
      paddedrotated[:,col] = 255

      # Rotate back to straight
      w, h = paddedrotated.shape
      M = cv2.getRotationMatrix2D((h/2,w/2),45,1)
      straight = cv2.warpAffine(paddedrotated,M,(h,w))

      # Remove padding and save to disk
      straight = straight[bw:-bw,bw:-bw]
      cv2.imwrite('result.png',straight)


      Note that you don't actually have to rotate the image back to straight and crop it back to its original size. You could actually stop after the first line that says:



      col = np.argmax(colsum)


      and use some elementary trigonometry to work out what that means in your original image.



      Here is the output:



      enter image description here





      Keywords: line detection, detect line, rotate, pad, border, projection, project, image, image processing, Python, OpenCV, affine, Hough






      share|improve this answer
























      • Awesome approach Mark, but a more general solution would be helpful because I can have images where there is no line at all(only some white dots), or lines which don't go all along the x axis. Added few more images in the original question.

        – pilogo
        Nov 28 '18 at 12:34











      • My other answer works fine for the new images. This answer also works fine for the new image with the partial line. If you don't want it to find anything in the image with no lines, you would need to think a minimum length threshold and apply it where the code says col = np.argmax(colsum)

        – Mark Setchell
        Nov 28 '18 at 12:53














      1












      1








      1







      I implemented a slightly simpler algorithm than my other answer but in Python with OpenCV this time.



      Basically, rather than taking the mean of vertical columns of pixels, it sums the pixels in the columns and chooses the column that is brightest. If I show the padded, rotated image with another image below representing the sums of the columns, you should see how it works:



      enter image description here



      #!/usr/bin/env python3

      import cv2
      import numpy as np

      # Load image as greyscale
      im = cv2.imread('45.jpg',cv2.IMREAD_GRAYSCALE)

      # Pad with border so it isn't cropped when rotated
      bw=300
      bordered = cv2.copyMakeBorder(im, top=bw, bottom=bw, left=bw, right=bw, borderType= cv2.BORDER_CONSTANT)

      # Rotate -45 degrees
      w, h = bordered.shape
      M = cv2.getRotationMatrix2D((h/2,w/2),-45,1)
      paddedrotated = cv2.warpAffine(bordered,M,(h,w))
      # DEBUG cv2.imwrite('1.tif',paddedrotated)

      # Sum the elements of each column and find column with most white pixels
      colsum = np.sum(paddedrotated,axis=0,dtype=np.float)
      col = np.argmax(colsum)
      # DEBUG cv2.imwrite('2.tif',colsum)

      # Fill with black except for the line we have located which we make white
      paddedrotated[:,:] = 0
      paddedrotated[:,col] = 255

      # Rotate back to straight
      w, h = paddedrotated.shape
      M = cv2.getRotationMatrix2D((h/2,w/2),45,1)
      straight = cv2.warpAffine(paddedrotated,M,(h,w))

      # Remove padding and save to disk
      straight = straight[bw:-bw,bw:-bw]
      cv2.imwrite('result.png',straight)


      Note that you don't actually have to rotate the image back to straight and crop it back to its original size. You could actually stop after the first line that says:



      col = np.argmax(colsum)


      and use some elementary trigonometry to work out what that means in your original image.



      Here is the output:



      enter image description here





      Keywords: line detection, detect line, rotate, pad, border, projection, project, image, image processing, Python, OpenCV, affine, Hough






      share|improve this answer













      I implemented a slightly simpler algorithm than my other answer but in Python with OpenCV this time.



      Basically, rather than taking the mean of vertical columns of pixels, it sums the pixels in the columns and chooses the column that is brightest. If I show the padded, rotated image with another image below representing the sums of the columns, you should see how it works:



      enter image description here



      #!/usr/bin/env python3

      import cv2
      import numpy as np

      # Load image as greyscale
      im = cv2.imread('45.jpg',cv2.IMREAD_GRAYSCALE)

      # Pad with border so it isn't cropped when rotated
      bw=300
      bordered = cv2.copyMakeBorder(im, top=bw, bottom=bw, left=bw, right=bw, borderType= cv2.BORDER_CONSTANT)

      # Rotate -45 degrees
      w, h = bordered.shape
      M = cv2.getRotationMatrix2D((h/2,w/2),-45,1)
      paddedrotated = cv2.warpAffine(bordered,M,(h,w))
      # DEBUG cv2.imwrite('1.tif',paddedrotated)

      # Sum the elements of each column and find column with most white pixels
      colsum = np.sum(paddedrotated,axis=0,dtype=np.float)
      col = np.argmax(colsum)
      # DEBUG cv2.imwrite('2.tif',colsum)

      # Fill with black except for the line we have located which we make white
      paddedrotated[:,:] = 0
      paddedrotated[:,col] = 255

      # Rotate back to straight
      w, h = paddedrotated.shape
      M = cv2.getRotationMatrix2D((h/2,w/2),45,1)
      straight = cv2.warpAffine(paddedrotated,M,(h,w))

      # Remove padding and save to disk
      straight = straight[bw:-bw,bw:-bw]
      cv2.imwrite('result.png',straight)


      Note that you don't actually have to rotate the image back to straight and crop it back to its original size. You could actually stop after the first line that says:



      col = np.argmax(colsum)


      and use some elementary trigonometry to work out what that means in your original image.



      Here is the output:



      enter image description here





      Keywords: line detection, detect line, rotate, pad, border, projection, project, image, image processing, Python, OpenCV, affine, Hough







      share|improve this answer












      share|improve this answer



      share|improve this answer










      answered Nov 28 '18 at 9:59









      Mark SetchellMark Setchell

      90.1k778180




      90.1k778180













      • Awesome approach Mark, but a more general solution would be helpful because I can have images where there is no line at all(only some white dots), or lines which don't go all along the x axis. Added few more images in the original question.

        – pilogo
        Nov 28 '18 at 12:34











      • My other answer works fine for the new images. This answer also works fine for the new image with the partial line. If you don't want it to find anything in the image with no lines, you would need to think a minimum length threshold and apply it where the code says col = np.argmax(colsum)

        – Mark Setchell
        Nov 28 '18 at 12:53



















      • Awesome approach Mark, but a more general solution would be helpful because I can have images where there is no line at all(only some white dots), or lines which don't go all along the x axis. Added few more images in the original question.

        – pilogo
        Nov 28 '18 at 12:34











      • My other answer works fine for the new images. This answer also works fine for the new image with the partial line. If you don't want it to find anything in the image with no lines, you would need to think a minimum length threshold and apply it where the code says col = np.argmax(colsum)

        – Mark Setchell
        Nov 28 '18 at 12:53

















      Awesome approach Mark, but a more general solution would be helpful because I can have images where there is no line at all(only some white dots), or lines which don't go all along the x axis. Added few more images in the original question.

      – pilogo
      Nov 28 '18 at 12:34





      Awesome approach Mark, but a more general solution would be helpful because I can have images where there is no line at all(only some white dots), or lines which don't go all along the x axis. Added few more images in the original question.

      – pilogo
      Nov 28 '18 at 12:34













      My other answer works fine for the new images. This answer also works fine for the new image with the partial line. If you don't want it to find anything in the image with no lines, you would need to think a minimum length threshold and apply it where the code says col = np.argmax(colsum)

      – Mark Setchell
      Nov 28 '18 at 12:53





      My other answer works fine for the new images. This answer also works fine for the new image with the partial line. If you don't want it to find anything in the image with no lines, you would need to think a minimum length threshold and apply it where the code says col = np.argmax(colsum)

      – Mark Setchell
      Nov 28 '18 at 12:53













      1














      I did this on the command-line in Terminal with ImageMagick but you can apply exactly the same technique with OpenCV.



      Step 1



      Take the image and rotate it 45 degrees introducing black pixels as background where required:



      convert 45.jpg -background black -rotate 45 result.png


      enter image description here



      Step 2



      Now, building on the previous command, set every pixel to the median of the box 1px wide and 250px tall centred on it:



      convert 45.jpg -background black -rotate 45 -statistic median 1x250 result.png


      enter image description here



      Step 3



      Now, again building on the previous command, rotate it back 45 degrees:



      convert 45.jpg -background black -rotate 45 -statistic median 1x250 -rotate -45 result.png


      enter image description here





      So, in summary, the entire processing is:



      convert input.jpg -background black -rotate 45 -statistic median 1x250 -rotate -45 result.png


      Obviously then crop it back to the original size and append side-by-side with the original for checking:



      convert 45.jpg -background black -rotate 45 -statistic median 5x250 -rotate -45 +repage -gravity center -crop 184x866+0+0 result.png
      convert 45.jpg result.png +append result.png


      enter image description here





      You can also use mean statistic plus thresholding rather than median since it is quicker than sorting to find the median, however it tends to lead to smearing:



      convert 45.jpg -background black -rotate 45 -statistic mean 1x250 result.png


      enter image description here



      Your newly-added image gets processed to this result:



      enter image description here






      share|improve this answer






























        1














        I did this on the command-line in Terminal with ImageMagick but you can apply exactly the same technique with OpenCV.



        Step 1



        Take the image and rotate it 45 degrees introducing black pixels as background where required:



        convert 45.jpg -background black -rotate 45 result.png


        enter image description here



        Step 2



        Now, building on the previous command, set every pixel to the median of the box 1px wide and 250px tall centred on it:



        convert 45.jpg -background black -rotate 45 -statistic median 1x250 result.png


        enter image description here



        Step 3



        Now, again building on the previous command, rotate it back 45 degrees:



        convert 45.jpg -background black -rotate 45 -statistic median 1x250 -rotate -45 result.png


        enter image description here





        So, in summary, the entire processing is:



        convert input.jpg -background black -rotate 45 -statistic median 1x250 -rotate -45 result.png


        Obviously then crop it back to the original size and append side-by-side with the original for checking:



        convert 45.jpg -background black -rotate 45 -statistic median 5x250 -rotate -45 +repage -gravity center -crop 184x866+0+0 result.png
        convert 45.jpg result.png +append result.png


        enter image description here





        You can also use mean statistic plus thresholding rather than median since it is quicker than sorting to find the median, however it tends to lead to smearing:



        convert 45.jpg -background black -rotate 45 -statistic mean 1x250 result.png


        enter image description here



        Your newly-added image gets processed to this result:



        enter image description here






        share|improve this answer




























          1












          1








          1







          I did this on the command-line in Terminal with ImageMagick but you can apply exactly the same technique with OpenCV.



          Step 1



          Take the image and rotate it 45 degrees introducing black pixels as background where required:



          convert 45.jpg -background black -rotate 45 result.png


          enter image description here



          Step 2



          Now, building on the previous command, set every pixel to the median of the box 1px wide and 250px tall centred on it:



          convert 45.jpg -background black -rotate 45 -statistic median 1x250 result.png


          enter image description here



          Step 3



          Now, again building on the previous command, rotate it back 45 degrees:



          convert 45.jpg -background black -rotate 45 -statistic median 1x250 -rotate -45 result.png


          enter image description here





          So, in summary, the entire processing is:



          convert input.jpg -background black -rotate 45 -statistic median 1x250 -rotate -45 result.png


          Obviously then crop it back to the original size and append side-by-side with the original for checking:



          convert 45.jpg -background black -rotate 45 -statistic median 5x250 -rotate -45 +repage -gravity center -crop 184x866+0+0 result.png
          convert 45.jpg result.png +append result.png


          enter image description here





          You can also use mean statistic plus thresholding rather than median since it is quicker than sorting to find the median, however it tends to lead to smearing:



          convert 45.jpg -background black -rotate 45 -statistic mean 1x250 result.png


          enter image description here



          Your newly-added image gets processed to this result:



          enter image description here






          share|improve this answer















          I did this on the command-line in Terminal with ImageMagick but you can apply exactly the same technique with OpenCV.



          Step 1



          Take the image and rotate it 45 degrees introducing black pixels as background where required:



          convert 45.jpg -background black -rotate 45 result.png


          enter image description here



          Step 2



          Now, building on the previous command, set every pixel to the median of the box 1px wide and 250px tall centred on it:



          convert 45.jpg -background black -rotate 45 -statistic median 1x250 result.png


          enter image description here



          Step 3



          Now, again building on the previous command, rotate it back 45 degrees:



          convert 45.jpg -background black -rotate 45 -statistic median 1x250 -rotate -45 result.png


          enter image description here





          So, in summary, the entire processing is:



          convert input.jpg -background black -rotate 45 -statistic median 1x250 -rotate -45 result.png


          Obviously then crop it back to the original size and append side-by-side with the original for checking:



          convert 45.jpg -background black -rotate 45 -statistic median 5x250 -rotate -45 +repage -gravity center -crop 184x866+0+0 result.png
          convert 45.jpg result.png +append result.png


          enter image description here





          You can also use mean statistic plus thresholding rather than median since it is quicker than sorting to find the median, however it tends to lead to smearing:



          convert 45.jpg -background black -rotate 45 -statistic mean 1x250 result.png


          enter image description here



          Your newly-added image gets processed to this result:



          enter image description here







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 28 '18 at 12:44

























          answered Nov 27 '18 at 18:01









          Mark SetchellMark Setchell

          90.1k778180




          90.1k778180























              1














              The problem is clearly that the line you are searching for is not a line. It looks actually like a train of connected circles and boxes. Therefore, I recommend that you do the following:



              Find all contours in the image using find contours



              img = cv.imread('image.jpg')
              img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
              ret, thresh = cv.threshold(img_gray, 127, 255, 0)
              img2, contours, hierarchy = cv.findContours(thresh, CHAIN_APPROX_SIMPLE ,cv.RETR_EXTERNAL)


              This will return many many contours, so use a loop to save only long enough contours. Since the image size is 814x1041 pixels, I assume the contour long if it is at least 10% of the image width which is almost 100 (you must apparently optimize this value)



              long_contours = 
              for contour in contours[i]:
              perimeter = cv2.arcLength(contour,True)
              if (perimeter > 0.1 * 1018) # 10% of the image width
              long_contours.append(contour)


              Now draw a rotated bounding rectangle around those long contours that might be a line as well. The long contour is considered a line if its width is much longer than its height, or its aspect ratio is large (such as 8, and you need also to optimize this value)



              for long_contour in long_contours:
              rect = cv2.minAreaRect(long_contour)
              aspec_ratio = rect.width / rect.height

              if aspec_ratio > 8 :
              box = cv2.boxPoints(rect)
              box = np.int0(box)
              cv2.drawContours(img,[box],0,(255,255,255),cv.FILLED)


              Finally you should get something like that. Please note the code here is for guidance only.



              Rotated rectangle over a suspected line






              share|improve this answer
























              • This is a good observation. I wish you hadn't used contours. That just makes no sense to me. You can do this much simpler and efficiently using connected component analysis. Contours are totally overused. You should only extract contours if you specifically need the contours of objects.

                – Cris Luengo
                Nov 28 '18 at 17:30











              • Thanks for the hint. I agree that using connected components could be easier and more efficient. There no specific advantage for using contours, except that I reuse them in many projects. I will change that in the future.

                – Sameh Yassin
                Nov 29 '18 at 8:13


















              1














              The problem is clearly that the line you are searching for is not a line. It looks actually like a train of connected circles and boxes. Therefore, I recommend that you do the following:



              Find all contours in the image using find contours



              img = cv.imread('image.jpg')
              img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
              ret, thresh = cv.threshold(img_gray, 127, 255, 0)
              img2, contours, hierarchy = cv.findContours(thresh, CHAIN_APPROX_SIMPLE ,cv.RETR_EXTERNAL)


              This will return many many contours, so use a loop to save only long enough contours. Since the image size is 814x1041 pixels, I assume the contour long if it is at least 10% of the image width which is almost 100 (you must apparently optimize this value)



              long_contours = 
              for contour in contours[i]:
              perimeter = cv2.arcLength(contour,True)
              if (perimeter > 0.1 * 1018) # 10% of the image width
              long_contours.append(contour)


              Now draw a rotated bounding rectangle around those long contours that might be a line as well. The long contour is considered a line if its width is much longer than its height, or its aspect ratio is large (such as 8, and you need also to optimize this value)



              for long_contour in long_contours:
              rect = cv2.minAreaRect(long_contour)
              aspec_ratio = rect.width / rect.height

              if aspec_ratio > 8 :
              box = cv2.boxPoints(rect)
              box = np.int0(box)
              cv2.drawContours(img,[box],0,(255,255,255),cv.FILLED)


              Finally you should get something like that. Please note the code here is for guidance only.



              Rotated rectangle over a suspected line






              share|improve this answer
























              • This is a good observation. I wish you hadn't used contours. That just makes no sense to me. You can do this much simpler and efficiently using connected component analysis. Contours are totally overused. You should only extract contours if you specifically need the contours of objects.

                – Cris Luengo
                Nov 28 '18 at 17:30











              • Thanks for the hint. I agree that using connected components could be easier and more efficient. There no specific advantage for using contours, except that I reuse them in many projects. I will change that in the future.

                – Sameh Yassin
                Nov 29 '18 at 8:13
















              1












              1








              1







              The problem is clearly that the line you are searching for is not a line. It looks actually like a train of connected circles and boxes. Therefore, I recommend that you do the following:



              Find all contours in the image using find contours



              img = cv.imread('image.jpg')
              img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
              ret, thresh = cv.threshold(img_gray, 127, 255, 0)
              img2, contours, hierarchy = cv.findContours(thresh, CHAIN_APPROX_SIMPLE ,cv.RETR_EXTERNAL)


              This will return many many contours, so use a loop to save only long enough contours. Since the image size is 814x1041 pixels, I assume the contour long if it is at least 10% of the image width which is almost 100 (you must apparently optimize this value)



              long_contours = 
              for contour in contours[i]:
              perimeter = cv2.arcLength(contour,True)
              if (perimeter > 0.1 * 1018) # 10% of the image width
              long_contours.append(contour)


              Now draw a rotated bounding rectangle around those long contours that might be a line as well. The long contour is considered a line if its width is much longer than its height, or its aspect ratio is large (such as 8, and you need also to optimize this value)



              for long_contour in long_contours:
              rect = cv2.minAreaRect(long_contour)
              aspec_ratio = rect.width / rect.height

              if aspec_ratio > 8 :
              box = cv2.boxPoints(rect)
              box = np.int0(box)
              cv2.drawContours(img,[box],0,(255,255,255),cv.FILLED)


              Finally you should get something like that. Please note the code here is for guidance only.



              Rotated rectangle over a suspected line






              share|improve this answer













              The problem is clearly that the line you are searching for is not a line. It looks actually like a train of connected circles and boxes. Therefore, I recommend that you do the following:



              Find all contours in the image using find contours



              img = cv.imread('image.jpg')
              img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
              ret, thresh = cv.threshold(img_gray, 127, 255, 0)
              img2, contours, hierarchy = cv.findContours(thresh, CHAIN_APPROX_SIMPLE ,cv.RETR_EXTERNAL)


              This will return many many contours, so use a loop to save only long enough contours. Since the image size is 814x1041 pixels, I assume the contour long if it is at least 10% of the image width which is almost 100 (you must apparently optimize this value)



              long_contours = 
              for contour in contours[i]:
              perimeter = cv2.arcLength(contour,True)
              if (perimeter > 0.1 * 1018) # 10% of the image width
              long_contours.append(contour)


              Now draw a rotated bounding rectangle around those long contours that might be a line as well. The long contour is considered a line if its width is much longer than its height, or its aspect ratio is large (such as 8, and you need also to optimize this value)



              for long_contour in long_contours:
              rect = cv2.minAreaRect(long_contour)
              aspec_ratio = rect.width / rect.height

              if aspec_ratio > 8 :
              box = cv2.boxPoints(rect)
              box = np.int0(box)
              cv2.drawContours(img,[box],0,(255,255,255),cv.FILLED)


              Finally you should get something like that. Please note the code here is for guidance only.



              Rotated rectangle over a suspected line







              share|improve this answer












              share|improve this answer



              share|improve this answer










              answered Nov 28 '18 at 15:24









              Sameh YassinSameh Yassin

              6916




              6916













              • This is a good observation. I wish you hadn't used contours. That just makes no sense to me. You can do this much simpler and efficiently using connected component analysis. Contours are totally overused. You should only extract contours if you specifically need the contours of objects.

                – Cris Luengo
                Nov 28 '18 at 17:30











              • Thanks for the hint. I agree that using connected components could be easier and more efficient. There no specific advantage for using contours, except that I reuse them in many projects. I will change that in the future.

                – Sameh Yassin
                Nov 29 '18 at 8:13





















              • This is a good observation. I wish you hadn't used contours. That just makes no sense to me. You can do this much simpler and efficiently using connected component analysis. Contours are totally overused. You should only extract contours if you specifically need the contours of objects.

                – Cris Luengo
                Nov 28 '18 at 17:30











              • Thanks for the hint. I agree that using connected components could be easier and more efficient. There no specific advantage for using contours, except that I reuse them in many projects. I will change that in the future.

                – Sameh Yassin
                Nov 29 '18 at 8:13



















              This is a good observation. I wish you hadn't used contours. That just makes no sense to me. You can do this much simpler and efficiently using connected component analysis. Contours are totally overused. You should only extract contours if you specifically need the contours of objects.

              – Cris Luengo
              Nov 28 '18 at 17:30





              This is a good observation. I wish you hadn't used contours. That just makes no sense to me. You can do this much simpler and efficiently using connected component analysis. Contours are totally overused. You should only extract contours if you specifically need the contours of objects.

              – Cris Luengo
              Nov 28 '18 at 17:30













              Thanks for the hint. I agree that using connected components could be easier and more efficient. There no specific advantage for using contours, except that I reuse them in many projects. I will change that in the future.

              – Sameh Yassin
              Nov 29 '18 at 8:13







              Thanks for the hint. I agree that using connected components could be easier and more efficient. There no specific advantage for using contours, except that I reuse them in many projects. I will change that in the future.

              – Sameh Yassin
              Nov 29 '18 at 8:13













              1














              Your original code is fine as a whistle. The only problem is that your image contains too many information which mess up the accumulator scores. Everything will work out if you increase the line threshold to 255.



              minLineLength = 150 
              maxLineGap = 5
              line_thr = 255
              linesP = cv2.HoughLinesP(dst, 1, np.pi / 180.0, line_thr, None, minLineLength, maxLineGap)


              Here are the results using that value.
              3 lines are detected here due to the large white pixel size.



              [  1  41 286 326]
              [ 0 42 208 250]
              [ 1 42 286 327]


              enter image description here



              5 lines are detected around the same area due to the same reason as above. Reducing the pixel sizes using morphological operation or distance transform should fix this.



              [110 392 121 598]
              [112 393 119 544]
              [141 567 147 416]
              [ 29 263 29 112]
              [ 0 93 179 272]


              enter image description here



              No line found here.
              enter image description here






              share|improve this answer




























                1














                Your original code is fine as a whistle. The only problem is that your image contains too many information which mess up the accumulator scores. Everything will work out if you increase the line threshold to 255.



                minLineLength = 150 
                maxLineGap = 5
                line_thr = 255
                linesP = cv2.HoughLinesP(dst, 1, np.pi / 180.0, line_thr, None, minLineLength, maxLineGap)


                Here are the results using that value.
                3 lines are detected here due to the large white pixel size.



                [  1  41 286 326]
                [ 0 42 208 250]
                [ 1 42 286 327]


                enter image description here



                5 lines are detected around the same area due to the same reason as above. Reducing the pixel sizes using morphological operation or distance transform should fix this.



                [110 392 121 598]
                [112 393 119 544]
                [141 567 147 416]
                [ 29 263 29 112]
                [ 0 93 179 272]


                enter image description here



                No line found here.
                enter image description here






                share|improve this answer


























                  1












                  1








                  1







                  Your original code is fine as a whistle. The only problem is that your image contains too many information which mess up the accumulator scores. Everything will work out if you increase the line threshold to 255.



                  minLineLength = 150 
                  maxLineGap = 5
                  line_thr = 255
                  linesP = cv2.HoughLinesP(dst, 1, np.pi / 180.0, line_thr, None, minLineLength, maxLineGap)


                  Here are the results using that value.
                  3 lines are detected here due to the large white pixel size.



                  [  1  41 286 326]
                  [ 0 42 208 250]
                  [ 1 42 286 327]


                  enter image description here



                  5 lines are detected around the same area due to the same reason as above. Reducing the pixel sizes using morphological operation or distance transform should fix this.



                  [110 392 121 598]
                  [112 393 119 544]
                  [141 567 147 416]
                  [ 29 263 29 112]
                  [ 0 93 179 272]


                  enter image description here



                  No line found here.
                  enter image description here






                  share|improve this answer













                  Your original code is fine as a whistle. The only problem is that your image contains too many information which mess up the accumulator scores. Everything will work out if you increase the line threshold to 255.



                  minLineLength = 150 
                  maxLineGap = 5
                  line_thr = 255
                  linesP = cv2.HoughLinesP(dst, 1, np.pi / 180.0, line_thr, None, minLineLength, maxLineGap)


                  Here are the results using that value.
                  3 lines are detected here due to the large white pixel size.



                  [  1  41 286 326]
                  [ 0 42 208 250]
                  [ 1 42 286 327]


                  enter image description here



                  5 lines are detected around the same area due to the same reason as above. Reducing the pixel sizes using morphological operation or distance transform should fix this.



                  [110 392 121 598]
                  [112 393 119 544]
                  [141 567 147 416]
                  [ 29 263 29 112]
                  [ 0 93 179 272]


                  enter image description here



                  No line found here.
                  enter image description here







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Nov 29 '18 at 14:31









                  yapws87yapws87

                  1,013213




                  1,013213






























                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53493709%2fopencv-line-detection-for-45-degree-lines%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      A CLEAN and SIMPLE way to add appendices to Table of Contents and bookmarks

                      Calculate evaluation metrics using cross_val_predict sklearn

                      Insert data from modal to MySQL (multiple modal on website)