อัลกอริทึมเพื่อตรวจจับมุมของแผ่นกระดาษในภาพถ่าย


102

วิธีใดที่ดีที่สุดในการตรวจจับมุมของใบแจ้งหนี้ / ใบเสร็จ / แผ่นกระดาษในภาพถ่าย สิ่งนี้จะใช้สำหรับการแก้ไขมุมมองในภายหลังก่อน OCR

แนวทางปัจจุบันของฉันคือ:

RGB> Grey> Canny Edge Detection with thresholding> Dilate (1)> Remove small objects (6)> clear boarder objects> pick larges blog ตาม Convex Area > [การตรวจจับมุม - ไม่ได้ใช้งาน]

ฉันอดไม่ได้ที่จะคิดว่าต้องมีวิธี 'อัจฉริยะ' / สถิติที่แข็งแกร่งกว่านี้เพื่อจัดการการแบ่งกลุ่มประเภทนี้ ฉันไม่มีตัวอย่างการฝึกอบรมมากนัก แต่ฉันน่าจะได้ 100 ภาพด้วยกัน

บริบทที่กว้างขึ้น:

ฉันใช้ matlab ในการสร้างต้นแบบและวางแผนที่จะใช้ระบบใน OpenCV และ Tesserect-OCR นี่เป็นปัญหาแรกในการประมวลผลภาพจำนวนมากที่ฉันต้องแก้ไขสำหรับแอปพลิเคชันเฉพาะนี้ ดังนั้นฉันจึงต้องการม้วนโซลูชันของตัวเองและทำความคุ้นเคยกับอัลกอริทึมการประมวลผลภาพอีกครั้ง

นี่คือภาพตัวอย่างบางส่วนที่ฉันต้องการให้อัลกอริทึมจัดการ: หากคุณต้องการรับมือกับความท้าทายสามารถดูภาพขนาดใหญ่ได้ที่http://madteckhead.com/tmp

กรณีที่ 1
(ที่มา: madteckhead.com )

กรณีที่ 2
(ที่มา: madteckhead.com )

กรณีที่ 3
(ที่มา: madteckhead.com )

กรณีที่ 4
(ที่มา: madteckhead.com )

ในกรณีที่ดีที่สุดสิ่งนี้ให้:

กรณีที่ 1 - แสนรู้
(ที่มา: madteckhead.com )

กรณีที่ 1 - โพสต์แสนรู้
(ที่มา: madteckhead.com )

กรณีที่ 1 - บล็อกที่ใหญ่ที่สุด
(ที่มา: madteckhead.com )

อย่างไรก็ตามมันล้มเหลวอย่างง่ายดายในกรณีอื่น ๆ :

กรณีที่ 2 - แสนรู้
(ที่มา: madteckhead.com )

กรณีที่ 2 - โพสต์แสนรู้
(ที่มา: madteckhead.com )

กรณีที่ 2 - บล็อกที่ใหญ่ที่สุด
(ที่มา: madteckhead.com )

ขอบคุณล่วงหน้าสำหรับแนวคิดดีๆทั้งหมด! ฉันรักมาก!

แก้ไข: Hough Transform Progress

ถาม: อัลกอริทึมใดที่จะรวมกลุ่มเส้นที่หยาบเพื่อหามุม ตามคำแนะนำจากคำตอบฉันสามารถใช้ Hough Transform เลือกบรรทัดและกรองได้ แนวทางปัจจุบันของฉันค่อนข้างหยาบ ฉันตั้งสมมติฐานว่าใบแจ้งหนี้มักจะมีค่าน้อยกว่า 15deg ซึ่งไม่สอดคล้องกับรูปภาพ ฉันได้ผลลัพธ์ที่สมเหตุสมผลสำหรับบรรทัดหากเป็นกรณีนี้ (ดูด้านล่าง) แต่ฉันไม่แน่ใจทั้งหมดเกี่ยวกับอัลกอริทึมที่เหมาะสมในการจัดกลุ่มเส้น (หรือโหวต) เพื่อคาดการณ์สำหรับมุม เส้น Hough ไม่ต่อเนื่อง และในภาพที่มีเสียงดังอาจมีเส้นขนานดังนั้นต้องมีรูปแบบหรือระยะห่างจากเมตริกจุดเริ่มต้นของเส้น ความคิดใด ๆ ?

กรณีที่ 1 กรณีที่ 2 กรณีที่ 3 กรณีที่ 4
(ที่มา: madteckhead.com )


1
ใช่ฉันใช้งานได้ประมาณ 95% ของกรณี ตั้งแต่นั้นมาฉันต้องเก็บรหัสเนื่องจากเวลาขาดแคลน ฉันจะโพสต์การติดตามผลในบางขั้นตอนโปรดอย่าลังเลที่จะมอบหมายให้ฉันหากคุณต้องการความช่วยเหลือเร่งด่วน ขออภัยที่ขาดการติดตามที่ดี ฉันอยากกลับไปใช้ฟีเจอร์นี้อีก
Nathan Keller

นาธานคุณช่วยโพสต์ติดตามว่าคุณจบลงได้อย่างไร? ฉันติดอยู่ที่จุดเดียวกับการรับรู้มุม / รูปทรงด้านนอกของแผ่นกระดาษ ฉันพบปัญหาเดียวกันกับที่คุณทำดังนั้นฉันจึงสนใจวิธีแก้ปัญหาอย่างมาก
ทิม

6
ภาพทั้งหมดในโพสต์นี้ตอนนี้
404.

คำตอบ:


29

ฉันเป็นเพื่อนของมาร์ตินที่ทำงานนี้เมื่อต้นปีนี้ นี่เป็นโครงการเขียนโค้ดครั้งแรกของฉันและก็จบลงด้วยความเร่งรีบดังนั้นรหัสจึงต้องมีข้อผิดพลาด ... ถอดรหัส ... ฉันจะให้คำแนะนำเล็ก ๆ น้อย ๆ จากสิ่งที่ฉันเคยเห็นคุณทำมาแล้ว จัดเรียงรหัสของฉันในวันหยุดพรุ่งนี้

เคล็ดลับแรกOpenCVและpythonที่ยอดเยี่ยมย้ายไปหาพวกเขาโดยเร็วที่สุด : ง

แทนที่จะลบวัตถุขนาดเล็กและหรือเสียงรบกวนให้ลดข้อ จำกัด ที่มีขนาดเล็กลงเพื่อให้รับขอบได้มากขึ้นจากนั้นหารูปทรงปิดที่ใหญ่ที่สุด (ใน OpenCV ใช้findcontour()กับพารามิเตอร์ง่ายๆฉันคิดว่าฉันใช้CV_RETR_LIST) อาจยังคงมีปัญหาเมื่ออยู่บนแผ่นกระดาษสีขาว แต่ก็ให้ผลลัพธ์ที่ดีที่สุด

สำหรับการHoughline2()แปลงร่างให้ลองใช้สิ่งCV_HOUGH_STANDARDที่ตรงข้ามกับค่าCV_HOUGH_PROBABILISTICนี้จะให้rhoและthetaกำหนดเส้นในพิกัดเชิงขั้วจากนั้นคุณสามารถจัดกลุ่มเส้นภายในขอบเขตที่ยอมรับได้

การจัดกลุ่มของฉันทำงานเป็นตารางค้นหาสำหรับแต่ละบรรทัดที่ส่งออกมาจากการแปลง hough มันจะให้คู่ rho และ theta หากค่าเหล่านี้อยู่ภายในให้พูดว่า 5% ของคู่ค่าในตารางค่าเหล่านี้จะถูกละทิ้งหากค่าเหล่านี้อยู่นอก 5% รายการใหม่จะถูกเพิ่มเข้าไปในตาราง

จากนั้นคุณสามารถวิเคราะห์เส้นขนานหรือระยะห่างระหว่างเส้นได้ง่ายขึ้นมาก

หวังว่านี่จะช่วยได้


สวัสดี Daniel ขอบคุณสำหรับการมีส่วนร่วม ฉันชอบให้คุณเข้าใกล้ มันเป็นเส้นทางที่ฉันได้รับผลลัพธ์ที่ดีในขณะนี้ มีแม้แต่และตัวอย่าง OpenCV ที่ตรวจพบรูปสี่เหลี่ยม เพียงแค่ต้องทำการกรองผลลัพธ์ อย่างที่คุณบอกว่าสีขาวบนสีขาวนั้นตรวจจับได้ยากด้วยวิธีนี้ แต่มันเป็นวิธีที่ง่ายและเสียค่าใช้จ่ายน้อยกว่าวิธีการ ฉันได้ละทิ้งแนวทางที่ยากลำบากออกไปจากอัลโกของฉันแล้วและทำการประมาณค่าโพลีดูตัวอย่างสี่เหลี่ยมใน opencv ฉันต้องการเห็นการดำเนินการของการลงคะแนนที่ยากลำบาก ขอบคุณล่วงหน้านาธาน
นาธานเคลเลอร์

ฉันมีปัญหากับแนวทางนี้ฉันจะโพสต์วิธีแก้ปัญหาหากฉันสามารถประดิษฐ์สิ่งที่ดีกว่าสำหรับการอ้างอิงในอนาคต
Anshuman Kumar

@AnshumanKumar ฉันต้องการความช่วยเหลือจริงๆสำหรับคำถามนี้คุณช่วยฉันได้ไหม stackoverflow.com/questions/61216402/…
Carlos Diego

19

กลุ่มนักศึกษาในมหาวิทยาลัยของฉันเพิ่งสาธิตแอป iPhone (และแอป python OpenCV) ที่พวกเขาเขียนขึ้นเพื่อทำสิ่งนี้ อย่างที่ฉันจำได้ขั้นตอนมีดังนี้:

  • ตัวกรองค่ามัธยฐานเพื่อลบข้อความบนกระดาษทั้งหมด (นี่คือข้อความที่เขียนด้วยลายมือบนกระดาษสีขาวที่มีแสงค่อนข้างดีและอาจใช้ไม่ได้กับข้อความที่พิมพ์ออกมามันทำงานได้ดีมาก) เหตุผลก็คือทำให้การตรวจจับมุมง่ายขึ้นมาก
  • Hough Transform สำหรับเส้น
  • ค้นหาจุดสูงสุดในพื้นที่สะสม Hough Transform แล้วลากเส้นแต่ละเส้นทั่วทั้งภาพ
  • วิเคราะห์เส้นและลบเส้นที่อยู่ใกล้กันมากและอยู่ในมุมที่ใกล้เคียงกัน (รวมเส้นเป็นเส้นเดียว) สิ่งนี้จำเป็นเนื่องจาก Hough Transform ไม่สมบูรณ์แบบเนื่องจากทำงานในพื้นที่ตัวอย่างที่ไม่ต่อเนื่อง
  • ค้นหาคู่ของเส้นที่ขนานกันโดยประมาณและตัดกับคู่อื่น ๆ เพื่อดูว่าเส้นใดเป็นสี่กลุ่ม

ดูเหมือนว่าจะทำงานได้ดีพอสมควรและพวกเขาสามารถถ่ายภาพกระดาษหรือหนังสือทำการตรวจจับมุมจากนั้นแมปเอกสารในภาพลงบนระนาบแบนในเกือบเรียลไทม์ (มีฟังก์ชัน OpenCV เพียงฟังก์ชันเดียว การทำแผนที่) ไม่มี OCR เมื่อฉันเห็นมันทำงาน


ขอบคุณสำหรับแนวคิดดีๆมาร์ติน ฉันได้รับคำแนะนำของคุณและนำแนวทางการเปลี่ยนแปลงของ Hough ไปใช้ (ดูผลลัพธ์ด้านบน) ฉันกำลังดิ้นรนเพื่อหาอัลกอริทึมที่มีประสิทธิภาพซึ่งจะคาดการณ์เส้นเพื่อหาจุดตัด มีไม่กี่บรรทัดและมีผลบวกเท็จเล็กน้อย คุณมีคำแนะนำเกี่ยวกับวิธีที่ดีที่สุดในการรวมและละทิ้งบรรทัดหรือไม่? หากนักเรียนของคุณสนใจโปรดสนับสนุนให้พวกเขาติดต่อ ฉันชอบที่จะได้ยินประสบการณ์ของพวกเขาในการทำให้อัลกอริทึมทำงานบนแพลตฟอร์มมือถือ (นั่นคือเป้าหมายต่อไปของฉัน) ขอบคุณมากสำหรับความคิดของคุณ
Nathan Keller

1
ดูเหมือนว่า HT สำหรับเส้นจะทำงานได้ดีในทุกภาพยกเว้นภาพที่สองของคุณ แต่คุณกำลังกำหนดขีด จำกัด สำหรับค่าเริ่มต้นและค่าสิ้นสุดในตัวสะสมหรือไม่? HT ไม่ได้กำหนดตำแหน่งเริ่มต้นและตำแหน่งสิ้นสุด แต่เป็นค่า m และ c ใน y = mx + c ดูที่นี่ - โปรดทราบว่าสิ่งนี้ใช้พิกัดเชิงขั้วในตัวสะสมมากกว่าคาร์ทีเซียน ด้วยวิธีนี้คุณสามารถจัดกลุ่มเส้นตาม c แล้วตามด้วย m เพื่อทำให้เส้นบาง ๆ และโดยการจินตนาการว่าเส้นนั้นขยายไปทั่วทั้งภาพคุณจะพบจุดตัดที่มีประโยชน์มากขึ้น
Martin Foot

@MartinFoot ฉันต้องการความช่วยเหลือจริงๆสำหรับคำถามนี้คุณช่วยฉันได้ไหม stackoverflow.com/questions/61216402/…
Carlos Diego

16

นี่คือสิ่งที่ฉันคิดขึ้นหลังจากการทดลองเล็กน้อย:

import cv, cv2, numpy as np
import sys

def get_new(old):
    new = np.ones(old.shape, np.uint8)
    cv2.bitwise_not(new,new)
    return new

if __name__ == '__main__':
    orig = cv2.imread(sys.argv[1])

    # these constants are carefully picked
    MORPH = 9
    CANNY = 84
    HOUGH = 25

    img = cv2.cvtColor(orig, cv2.COLOR_BGR2GRAY)
    cv2.GaussianBlur(img, (3,3), 0, img)


    # this is to recognize white on white
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(MORPH,MORPH))
    dilated = cv2.dilate(img, kernel)

    edges = cv2.Canny(dilated, 0, CANNY, apertureSize=3)

    lines = cv2.HoughLinesP(edges, 1,  3.14/180, HOUGH)
    for line in lines[0]:
         cv2.line(edges, (line[0], line[1]), (line[2], line[3]),
                         (255,0,0), 2, 8)

    # finding contours
    contours, _ = cv2.findContours(edges.copy(), cv.CV_RETR_EXTERNAL,
                                   cv.CV_CHAIN_APPROX_TC89_KCOS)
    contours = filter(lambda cont: cv2.arcLength(cont, False) > 100, contours)
    contours = filter(lambda cont: cv2.contourArea(cont) > 10000, contours)

    # simplify contours down to polygons
    rects = []
    for cont in contours:
        rect = cv2.approxPolyDP(cont, 40, True).copy().reshape(-1, 2)
        rects.append(rect)

    # that's basically it
    cv2.drawContours(orig, rects,-1,(0,255,0),1)

    # show only contours
    new = get_new(img)
    cv2.drawContours(new, rects,-1,(0,255,0),1)
    cv2.GaussianBlur(new, (9,9), 0, new)
    new = cv2.Canny(new, 0, CANNY, apertureSize=3)

    cv2.namedWindow('result', cv2.WINDOW_NORMAL)
    cv2.imshow('result', orig)
    cv2.waitKey(0)
    cv2.imshow('result', dilated)
    cv2.waitKey(0)
    cv2.imshow('result', edges)
    cv2.waitKey(0)
    cv2.imshow('result', new)
    cv2.waitKey(0)

    cv2.destroyAllWindows()

ไม่สมบูรณ์แบบ แต่อย่างน้อยก็ใช้ได้กับทุกตัวอย่าง:

1 2 3 4


4
ฉันกำลังทำโครงการที่คล้ายกัน ฉันทำงานเหนือรหัสและทำให้เกิดข้อผิดพลาด "No module named cv" ฉันติดตั้งเวอร์ชัน Open CV 2.4 และนำเข้า cv2 ทำงานได้อย่างสมบูรณ์แบบสำหรับฉัน
Navneet Singh

คุณใจดีพอที่จะอัปเดตโค้ดนี้เพื่อให้ใช้งานได้หรือไม่? pastebin.com/PMH5Y0M8มันทำให้ผมหน้าดำ
the7erm

คุณมีความคิดเกี่ยวกับวิธีการแปลงรหัสต่อไปนี้เป็น java: for line in lines[0]: cv2.line(edges, (line[0], line[1]), (line[2], line[3]), (255,0,0), 2, 8) # finding contours contours, _ = cv2.findContours(edges.copy(), cv.CV_RETR_EXTERNAL, cv.CV_CHAIN_APPROX_TC89_KCOS) contours = filter(lambda cont: cv2.arcLength(cont, False) > 100, contours) contours = filter(lambda cont: cv2.contourArea(cont) > 10000, contours)
aurelianr

Vanuan ฉันต้องการความช่วยเหลือเกี่ยวกับคำถามนี้จริงๆคุณช่วยฉันได้ไหม stackoverflow.com/questions/61216402/…
Carlos Diego

9

แทนที่จะเริ่มจากการตรวจจับขอบคุณสามารถใช้การตรวจจับมุม

Marvin Frameworkจัดเตรียมการใช้งานอัลกอริทึม Moravec เพื่อจุดประสงค์นี้ คุณสามารถหามุมของกระดาษเป็นจุดเริ่มต้น ด้านล่างผลลัพธ์ของอัลกอริทึมของ Moravec:

ป้อนคำอธิบายภาพที่นี่


4

นอกจากนี้คุณสามารถใช้MSER (ขอบเขตสูงสุดที่เสถียรสูงสุด) บนผลลัพธ์ของตัวดำเนินการ Sobel เพื่อค้นหาพื้นที่ที่เสถียรของภาพ สำหรับแต่ละพื้นที่ที่ MSER ส่งคืนคุณสามารถใช้การประมาณตัวถังนูนและโพลีเพื่อให้ได้สิ่งนี้

แต่การตรวจจับแบบนี้มีประโยชน์สำหรับการตรวจจับแบบสดมากกว่าภาพเดียวที่ไม่ได้ให้ผลลัพธ์ที่ดีที่สุดเสมอไป

ผลลัพธ์


1
คุณสามารถแบ่งปันรายละเอียดเพิ่มเติมเกี่ยวกับรหัสนี้ได้หรือไม่ขอบคุณหลาย ๆ กลุ่มล่วงหน้า
Monty

ฉันได้รับข้อผิดพลาดใน cv2 CHAIN_APPROX_SIMPLE แจ้งว่ามีค่ามากเกินไปที่จะคลายแพ็ก ความคิดใด ๆ ? ฉันใช้รูปภาพ 1024 * 1024 เป็นตัวอย่าง
Praveen

1
ขอขอบคุณทุกคนเพิ่งคิดออกจากการเปลี่ยนแปลงของไวยากรณ์ในสาขา OpenCV ปัจจุบันanswers.opencv.org/question/40329/...
ปวีณ

MSER ไม่ได้หมายถึงการแยก blobs? ฉันลองแล้วตรวจพบข้อความส่วนใหญ่เท่านั้น
Anshuman Kumar

3

หลังจากตรวจจับขอบแล้วให้ใช้ Hough Transform จากนั้นใส่จุดเหล่านั้นใน SVM (เครื่องเวกเตอร์ที่รองรับ) พร้อมกับป้ายกำกับหากตัวอย่างมีเส้นเรียบ SVM จะไม่มีปัญหาในการแบ่งส่วนที่จำเป็นของตัวอย่างและส่วนอื่น ๆ คำแนะนำของฉันเกี่ยวกับ SVM ใส่พารามิเตอร์เช่นการเชื่อมต่อและความยาว นั่นคือถ้าจุดเชื่อมต่อกันและมีความยาวก็น่าจะเป็นบรรทัดของใบเสร็จ จากนั้นคุณสามารถกำจัดจุดอื่น ๆ ทั้งหมด


สวัสดี Ares ขอบคุณสำหรับความคิดของคุณ! ฉันได้ใช้ Hough transform แล้ว (ดูด้านบน) ฉันไม่สามารถหาวิธีที่ชัดเจนในการหามุมที่ได้รับผลบวกปลอมและเส้นที่ไม่ต่อเนื่อง คุณมีแนวคิดเพิ่มเติมหรือไม่? ผ่านมาสักพักแล้วที่ฉันดูเทคนิค SVM นี่เป็นแนวทางภายใต้การดูแลหรือไม่? ฉันไม่มีข้อมูลการฝึกอบรม แต่ฉันสามารถสร้างได้ ฉันสนใจที่จะสำรวจแนวทางนี้เพราะฉันอยากเรียนรู้เพิ่มเติมเกี่ยวกับ SVM คุณสามารถแนะนำแหล่งข้อมูลใด ๆ ขอแสดงความนับถือ. Nathan
Nathan Keller

3

ที่นี่คุณมีรหัสของ @Vanuan โดยใช้ C ++:

cv::cvtColor(mat, mat, CV_BGR2GRAY);
cv::GaussianBlur(mat, mat, cv::Size(3,3), 0);
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Point(9,9));
cv::Mat dilated;
cv::dilate(mat, dilated, kernel);

cv::Mat edges;
cv::Canny(dilated, edges, 84, 3);

std::vector<cv::Vec4i> lines;
lines.clear();
cv::HoughLinesP(edges, lines, 1, CV_PI/180, 25);
std::vector<cv::Vec4i>::iterator it = lines.begin();
for(; it!=lines.end(); ++it) {
    cv::Vec4i l = *it;
    cv::line(edges, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(255,0,0), 2, 8);
}
std::vector< std::vector<cv::Point> > contours;
cv::findContours(edges, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_TC89_KCOS);
std::vector< std::vector<cv::Point> > contoursCleaned;
for (int i=0; i < contours.size(); i++) {
    if (cv::arcLength(contours[i], false) > 100)
        contoursCleaned.push_back(contours[i]);
}
std::vector<std::vector<cv::Point> > contoursArea;

for (int i=0; i < contoursCleaned.size(); i++) {
    if (cv::contourArea(contoursCleaned[i]) > 10000){
        contoursArea.push_back(contoursCleaned[i]);
    }
}
std::vector<std::vector<cv::Point> > contoursDraw (contoursCleaned.size());
for (int i=0; i < contoursArea.size(); i++){
    cv::approxPolyDP(Mat(contoursArea[i]), contoursDraw[i], 40, true);
}
Mat drawing = Mat::zeros( mat.size(), CV_8UC3 );
cv::drawContours(drawing, contoursDraw, -1, cv::Scalar(0,255,0),1);

นิยามตัวแปรของเส้นอยู่ที่ไหน? ต้องเป็น std :: vector <cv :: Vec4i> lines;
สามารถÜrek

@ CanÜrekคุณพูดถูก std::vector<cv::Vec4i> lines;ถูกประกาศในขอบเขตทั่วโลกในโครงการของฉัน
GBF_Gabriel

1
  1. แปลงเป็นพื้นที่ห้องปฏิบัติการ

  2. ใช้คลัสเตอร์ kmeans เซ็กเมนต์ 2

  3. จากนั้นใช้รูปทรงหรือปั้นบนหนึ่งในคลัสเตอร์ (ภายใน)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.