การตรวจจับ "แม่น้ำ" ในข้อความ


175

ในการแลกเปลี่ยนเท็กซ์ TeX เราได้พูดคุยกันถึงวิธีการตรวจจับ "แม่น้ำ" ในย่อหน้าในคำถามนี้

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

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

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

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

แก้ไข: endolith ถามว่าทำไมฉันกำลังทำตามวิธีการประมวลผลภาพเนื่องจากใน TeX เราสามารถเข้าถึงตำแหน่ง glyph, spacings และอื่น ๆ และมันอาจเร็วและเชื่อถือได้มากขึ้นในการใช้อัลกอริทึมที่ตรวจสอบข้อความจริง เหตุผลของฉันในการทำสิ่งต่าง ๆ ก็คือรูปร่างของร่ายมนตร์สามารถส่งผลกระทบต่อวิธีการที่เห็นได้ชัดเจนแม่น้ำและในระดับข้อความมันเป็นเรื่องยากมากที่จะพิจารณารูปร่างนี้ (ซึ่งขึ้นอยู่กับตัวอักษรในการให้ยืม ฯลฯ ) สำหรับตัวอย่างว่ารูปร่างของร่ายมนตร์มีความสำคัญอย่างไรให้พิจารณาตัวอย่างสองตัวอย่างต่อไปนี้ซึ่งความแตกต่างระหว่างพวกเขาคือฉันได้แทนที่ร่ายมนตร์สองสามตัวกับคนอื่นที่มีความกว้างเกือบเท่ากันดังนั้นการวิเคราะห์แบบข้อความจะพิจารณา พวกเขาเท่าเทียมกันดี / ไม่ดี อย่างไรก็ตามโปรดทราบว่าแม่น้ำในตัวอย่างแรกนั้นเลวร้ายยิ่งกว่าในแม่น้ำสายที่สอง

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

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


5
+1 ฉันชอบคำถามนี้ ความคิดแรกของฉันคือHough Transformแต่อาจต้องมีการประมวลผลล่วงหน้า อาจจะเป็นตัวกรองการขยายครั้งแรก
เก็บข้อมูล

ฉันประหลาดใจที่ Radon transform ไม่ทำงานจริง ๆ คุณทำได้อย่างไร?
endolith

@endolith: ไม่มีอะไรซับซ้อน ฉันใช้ImageLines[]จาก Mathematica โดยมีและไม่มี preprocessing ฉันเดาว่านี่เป็นเทคนิคการใช้ Hough มากกว่าการแปลงเรดอน ฉันจะไม่แปลกใจถ้าการประมวลผลล่วงหน้าที่เหมาะสม (ฉันไม่ได้ลองใช้ตัวกรองการขยายที่แนะนำของ datageist) และ / หรือการตั้งค่าพารามิเตอร์สามารถทำให้การทำงานนี้
เลฟบิชอป

Google Image Search สำหรับแม่น้ำก็แสดงแม่น้ำ "ที่คดเคี้ยว" เช่นกัน คุณต้องการค้นหามันไหม? cdn.ilovetypography.com/img/text-river1.gif
endolith

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

คำตอบ:


135

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

(1) เปิดภาพด้วยหน้ากาก nPix-by-1 โดยที่ nPix อยู่ห่างจากตัวอักษรในแนวตั้ง

#% read image
img = rgb2gray('http://i.stack.imgur.com/4ShOW.png');

%# threshold and open with a rectangle
%# that is roughly letter sized
bwImg = img > 200; %# threshold of 200 is better than 128

opImg = imopen(bwImg,ones(13,1));

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

(2) เปิดภาพด้วยหน้ากากขนาด 1 คูณ mPix เพื่อกำจัดสิ่งที่แคบเกินไปที่จะเป็นแม่น้ำ

opImg = imopen(opImg,ones(1,5));

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

(3) ลบ "แม่น้ำและทะเลสาบ" แนวนอนที่เกิดจากช่องว่างระหว่างย่อหน้าหรือเยื้อง สำหรับสิ่งนี้เราจะลบแถวทั้งหมดที่เป็นจริงทั้งหมดและเปิดด้วยหน้ากาก nPix-by-1 ที่เรารู้ว่าจะไม่ส่งผลกระทบต่อแม่น้ำที่เราพบก่อนหน้านี้

ในการลบทะเลสาบเราสามารถใช้หน้ากากเปิดที่มีขนาดใหญ่กว่า nPix-by-nPix เล็กน้อย

ในขั้นตอนนี้เรายังสามารถทิ้งทุกสิ่งที่เล็กเกินไปที่จะเป็นแม่น้ำจริงเช่นทุกสิ่งที่ครอบคลุมพื้นที่น้อยกว่า (nPix + 2) * (mPix + 2) * 4 (ที่จะให้เรา ~ 3 บรรทัด) +2 นั้นอยู่ที่นั่นเพราะเรารู้ว่าวัตถุทั้งหมดมีความสูงอย่างน้อย nPix และความกว้าง mPix และเราต้องการที่จะสูงกว่านั้นเล็กน้อย

%# horizontal river: just look for rows that are all true
opImg(all(opImg,2),:) = false;
%# open with line spacing (nPix)
opImg = imopen(opImg,ones(13,1));

%# remove lakes with nPix+2
opImg = opImg & ~imopen(opImg,ones(15,15)); 

%# remove small fry
opImg = bwareaopen(opImg,7*15*4);

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

(4) ถ้าเราสนใจไม่เพียง แต่ความยาว แต่ยังรวมถึงความกว้างของแม่น้ำเราสามารถรวมการแปลงระยะทางกับโครงกระดูก

   dt = bwdist(~opImg);
   sk = bwmorph(opImg,'skel',inf);
   %# prune the skeleton a bit to remove branches
   sk = bwmorph(sk,'spur',7);

   riversWithWidth = dt.*sk;

ป้อนคำอธิบายรูปภาพที่นี่ (สีตรงกับความกว้างของแม่น้ำ (แม้ว่าแถบสีจะปิดลงด้วยปัจจัย 2)

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


นี่คือการวิเคราะห์แบบเดียวกันกับภาพที่สอง "ไม่มีแม่น้ำ":

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


ขอบคุณ ฉันมี Matlab ดังนั้นฉันจะลองใช้ข้อความอื่นเพื่อดูว่ามันจะแข็งแกร่งแค่ไหน
เลฟบิชอป

หากต้องการรวมเข้ากับ TeX อาจเป็นปัญหาอีกอย่างหนึ่งยกเว้นว่าเราสามารถย้ายพอร์ตนั้นไปยัง Lua ได้
ℝaphink

@ เลฟบิชอป: ฉันคิดว่าฉันเข้าใจปัญหานี้ดีขึ้นเล็กน้อย โซลูชันใหม่ควรมีความแข็งแกร่งพอสมควร
Jonas

@levBishop: อีกหนึ่งการอัปเดต
Jonas

1
@LevBishop: เพิ่งสังเกตเห็นภาพที่สอง ปรากฎว่าการวิเคราะห์โดยใช้สัณฐานวิทยาทำงานได้
Jonas

56

ใน Mathematica ใช้การชะล้างและการแปลง Hough:

(*Get Your Images*)
i = Import /@ {"http://i.stack.imgur.com/4ShOW.png", 
               "http://i.stack.imgur.com/5UQwb.png"};

(*Erode and binarize*)
i1 = Binarize /@ (Erosion[#, 2] & /@ i);

(*Hough transform*)
lines = ImageLines[#, .5, "Segmented" -> True] & /@ i1;

(*Ready, show them*)
Show[#[[1]],Graphics[{Thick,Orange, Line /@ #[[2]]}]] & /@ Transpose[{i, lines}]

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

แก้ไขคำติชมของ Mr. Wizard

หากคุณต้องการกำจัดเส้นแนวนอนให้ทำสิ่งนี้แทน (อาจมีคนทำให้มันง่ายขึ้น):

Show[#[[1]], Graphics[{Thick, Orange, Line /@ #[[2]]}]] & /@ 
 Transpose[{i, Select[Flatten[#, 1], Chop@Last@(Subtract @@ #) != 0 &] & /@ lines}]

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


1
ทำไมไม่กำจัดเส้นแนวนอนทั้งหมด? (+1)
Mr.Wizard

@นาย. เพียงเพื่อแสดงทุกสายจะถูกตรวจพบ ...
ดร. เบลิซาเรี

1
นั่นไม่ใช่ส่วนหนึ่งของปัญหาอย่างไรก็ตามมันคืออะไร?
Mr.Wizard

@นาย. แก้ไขตามที่ร้องขอ
ดร. เบลิซาเรี

4
@belisarius ระบบพิกัดที่ใช้ในการแปลง Hough เปลี่ยนไปหลังจาก 8.0.0 เพื่อให้ตรงกับการแปลงเรดอนอย่างใดอย่างหนึ่ง ในทางกลับกันนี้ได้เปลี่ยนพฤติกรรมของ ImageLines โดยรวมนี่เป็นการปรับปรุงแม้ว่าในกรณีนี้เราจะชอบพฤติกรรมก่อนหน้านี้มากกว่า หากคุณไม่ต้องการที่จะทดสอบกับการตรวจจับสูงสุดคุณสามารถเปลี่ยนอัตราส่วนของภาพที่นำเข้าจะใกล้ชิดกับ 1 และได้รับผลคล้ายกับ lines = ImageLines[ImageResize[#, {300, 300}], .6, "Segmented" -> True] & /@ i1;8.0.0: ทั้งหมดที่กล่าวว่าสำหรับปัญหานี้วิธีการทางสัณฐานวิทยาดูเหมือนแข็งแกร่งมากขึ้น
Matthias Odisio

29

อืม ... ฉันเดาว่าการแปลงเรดอนนั้นไม่ใช่เรื่องง่ายที่จะดึงออกมา (โดยทั่วไปการแปลงเรดอนจะหมุนภาพในขณะที่ "มองผ่าน" ขอบเป็นหลักการที่อยู่เบื้องหลังการสแกน CAT) การแปลงภาพของคุณสร้างภาพนี้โดยใช้ "แม่น้ำ" ก่อให้เกิดจุดยอดที่สดใสซึ่งอยู่ในวงกลม:

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

การหมุนที่ 70 องศาสามารถเห็นได้อย่างชัดเจนว่ายอดเขาทางด้านซ้ายของพล็อตของชิ้นนี้ตามแนวแกนนอน:

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

โดยเฉพาะอย่างยิ่งถ้าข้อความเป็นแบบเกาส์เบลอก่อน:

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

แต่ฉันไม่แน่ใจว่าจะสกัดยอดเหล่านี้อย่างน่าเชื่อถือจากเสียงรบกวนที่เหลือได้อย่างไร ปลายด้านบนและล่างที่สดใสของรูปสัญลักษณ์แสดงถึง "แม่น้ำ" ระหว่างเส้นแนวนอนของข้อความซึ่งคุณไม่ต้องสนใจ อาจเป็นฟังก์ชั่นการถ่วงน้ำหนักเทียบกับมุมที่เน้นเส้นแนวตั้งมากขึ้น

ฟังก์ชั่นการคำนวณน้ำหนักแบบโคไซน์อย่างง่ายใช้งานได้ดีกับภาพนี้:

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

ค้นหาแม่น้ำในแนวดิ่งที่ 90 องศาซึ่งเป็นค่าสูงสุดทั่วโลกในรูปสัญลักษณ์:

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

และในภาพนี้การค้นหาที่ 104 องศาแม้ว่าการเบลอครั้งแรกทำให้แม่นยำยิ่งขึ้น:

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

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

แต่มันไม่พบจุดสูงสุดหลักสองอย่างใดอย่างหนึ่งในรูปสัญลักษณ์สำหรับภาพของคุณหลังจากการเบลอและการถ่วงน้ำหนัก:

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

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

from pylab import *
from scipy.misc import radon
import Image

filename = 'rivers.png'
I = asarray(Image.open(filename).convert('L').rotate(90))

# Do the radon transform and display the result
a = radon(I, theta = mgrid[0:180])

# Remove offset
a = a - min(a.flat)

# Weight it to emphasize vertical lines
b = arange(shape(a)[1]) #
d = (0.5-0.5*cos(b*pi/90))*a

figure()
imshow(d.T)
gray()
show()

# Find the global maximum, plot it, print it
peak_x, peak_y = unravel_index(argmax(d),shape(d))
plot(peak_x, peak_y,'ro')
print len(d)- peak_x, 'pixels', peak_y, 'degrees'

ถ้าคุณต้องเบลอด้วย Gaussian แบบไม่สมมาตรก่อนล่ะ คือแคบในแนวนอนกว้างในแนวตั้ง
Jonas

@ Jonas: นั่นอาจจะช่วยได้ ปัญหาหลักคือการเลือกยอดโดยอัตโนมัติจากพื้นหลังเมื่อพื้นหลังแตกต่างกันมากกับการหมุน การเบลอแบบอสมมาตรสามารถทำให้แถบขวางในแนวนอนราบเรียบจากบรรทัดหนึ่งไปอีกบรรทัด
endolith

วิธีนี้ใช้งานได้ดีสำหรับการตรวจจับการหมุนของบรรทัดในข้อความอย่างน้อย: gist.github.com/endolith/334196bac1cac45a4893
endolith

16

ฉันได้ฝึกการจำแนกประเภทลักษณนามสำหรับพิกเซลโดยใช้คุณสมบัติอนุพันธ์ (สูงสุดถึงลำดับที่ 2) ในระดับที่แตกต่างกัน

ป้ายกำกับของฉัน:

การติดฉลาก

การทำนายภาพการฝึกอบรม:

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

การทำนายภาพสองภาพอื่น ๆ :

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

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

ฉันเดาว่านี่จะดูมีแนวโน้มและสามารถให้ผลลัพธ์ที่ใช้งานได้เนื่องจากข้อมูลการฝึกอบรมเพิ่มเติม ในขณะที่ฉันใช้เวลาเพียงไม่กี่นาทีเพื่อให้ได้ผลลัพธ์เหล่านี้ คุณอาจจะทำซ้ำผลการค้นหาในตัวเองโดยการใช้ซอฟต์แวร์โอเพนซอร์สilastik [ข้อจำกัดความรับผิดชอบ: ฉันเป็นหนึ่งในนักพัฒนาหลัก]


2

(ขออภัยโพสต์นี้ไม่มีการสาธิตที่ยอดเยี่ยม)

หากคุณต้องการทำงานกับข้อมูลที่ TeX มีอยู่แล้ว (ตัวอักษรและตำแหน่ง) คุณสามารถจัดประเภทตัวอักษรและคู่จดหมายด้วยตนเองเป็น "ลาด" ในทิศทางเดียวหรืออีกทิศทางหนึ่ง ตัวอย่างเช่น "w" มีทางลาดมุม SW และ SE, คอมโบ "al" มีความชันมุม NW "k" มีความชันมุม NE (อย่าลืมเครื่องหมายวรรคตอน - เครื่องหมายคำพูดตามด้วยตัวอักษรที่เติมครึ่งล่างของกล่องสัญลักษณ์สร้างความชันที่ดี; เครื่องหมายคำพูดตามด้วย q นั้นแข็งแกร่งเป็นพิเศษ)

จากนั้นค้นหาการเกิดขึ้นของความลาดชันที่สอดคล้องกันในด้านตรงข้ามของช่องว่าง - "w al" สำหรับแม่น้ำ SW-to-NE หรือ "k T" สำหรับแม่น้ำ NW-to-SE เมื่อคุณพบอันใดอันหนึ่งบนบรรทัดให้ดูว่ามีสิ่งที่คล้ายกันเกิดขึ้นเลื่อนไปทางซ้ายหรือขวาอย่างเหมาะสมบนบรรทัดด้านบน / ล่าง เมื่อคุณพบสิ่งเหล่านี้อาจมีแม่น้ำ

นอกจากนี้เห็นได้ชัดว่าเพียงมองหาช่องว่างที่ซ้อนกันเกือบเป็นแนวตั้งสำหรับแม่น้ำในแนวดิ่ง

คุณสามารถเพิ่มความซับซ้อนได้เล็กน้อยโดยการวัด "ความแข็งแรง" ของความลาดชัน: กล่องล่วงหน้าจำนวนเท่าไหร่ก็ "ว่างเปล่า" เนื่องจากความลาดชันและมีส่วนทำให้ความกว้างของแม่น้ำ "w" มีขนาดค่อนข้างเล็กเนื่องจากมันมีเพียงมุมเล็ก ๆ ของกล่องล่วงหน้าที่มีส่วนต่อแม่น้ำ แต่ "V" นั้นแข็งแกร่งมาก "b" นั้นแข็งแกร่งกว่า "k" เล็กน้อย เส้นโค้งที่อ่อนโยนขึ้นทำให้ขอบแม่น้ำต่อเนื่องทางสายตามากขึ้นทำให้แข็งแรงและกว้างขึ้น

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