【问题标题】:How to accurately acquire line segments from the projection plot?如何从投影图中准确获取线段?
【发布时间】:2015-07-28 12:00:09
【问题描述】:

所以这基本上是一件非常简单的事情,就像只获取水平投影图并从中获取图像上线条的位置。但问题是所应用的阈值是非常可变的。如果我保持在安全级别,则会提取正确数量的行,而另一方面会提取不需要的结果。

例如这里是图片:

及其水平投影:

这是我用来提取文本行的代码:

%complementing as text must be non zero and background should be 0
img_comp = imcomplement(img);

%calculate the horizontal projections and plot it to verify the threshold
horizontal_projections = sum(img_comp, 2);
plot(horizontal_projections)

%A very crude method of automatically detecting the threshold

proj_mean = mean(horizontal_projections);
lines = horizontal_projections > floor(proj_mean); 

% Find Rising and falling edges
d = diff(lines);
startingColumns = find(d>0);
endingColumns = find(d<0);

% Extract each line and save it in a cell
for lines_k = 1 : length(startingColumns)
  lines_extracted{lines_k} = img(startingColumns(lines_k):endingColumns(lines_k), :);
end

我想自动选择阈值,但遇到了麻烦,如果我使用代码中显示的阈值作为预测的平均值,它会提取 9 行正确但这些行会丢失大量数据,如:

这是第二行,字母的延长线和降线都被剪掉了。使用平均值的一半或三分之一是可行的,但它对每张图像都不同,而且根本不会自动化。

【问题讨论】:

    标签: image matlab image-processing image-segmentation text-segmentation


    【解决方案1】:

    如何转换为 YCbCr 色彩空间?使用维基百科的转换公式。

    img = im2double(imread('StackOverflow-Example.jpg'));
    rp = img(:, :, 1) / 255 ;
    bp = img(:, :, 2) / 255 ;
    gp = img(:, :, 3) / 255 ;
    kb = 0.114;
    kr = 0.299;
    y = kr * rp + (1 - kr - kb) * gp + kb * bp;
    y = max(max(y))-y;
    y = y ./ y;
    surf(y,'EdgeColor','none','LineStyle','none')
    view(0, -90)
    

    维护信息看起来不错。

    编辑:

    我想你想要每一行

    %% Load image and find intensity %%
    img = im2double(imread('test.jpg')); % load image and convert to doubles to allow for calculations
    rp = img(:, :, 1) / 255 ; % normalized red portion
    bp = img(:, :, 2) / 255 ; % normalized blue portion
    gp = img(:, :, 3) / 255 ; % normalized green portion
    kb = 0.114; % blue constant from Wikipedia
    kr = 0.299; % red constant from Wikipedia
    x = kr * rp + (1 - kr - kb) * gp + kb * bp; % normalized intensity in image
    x = max(max(x))-x; % removed background
    
    y = x ./ x; % everything left is high
    
    z = y;
    z(isnan(y)) = 0; % turn nan's to zero
    divisions = find(sum(z,2) > 5); % find all lines that have less than 5 pixels
    divisions = [divisions(1); divisions(diff(divisions) > 10); size(z, 1)]; % find the line breaks
    
    rows = cell(length(divisions), 1);
    
    for i = 1:numel(rows)-1
        line = z(divisions(i):divisions(i+1), :); % grab line
        j = divisions(i) + find(sum(line,2) > 5) - 1; % remove the white space
        line = y(j, :);
        rows{i} = line; %store the line
    end
    
    rows(numel(rows)) = [];
    
    %% plot each line %%
    for i = 1:numel(rows) ; 
        figure(i) ; 
        surf(rows{i},'EdgeColor','none','LineStyle','none');
        view(0, -90) ;
    end
    
    %% plot entire page %%
    figure(numel(rows) + 1)
    surf(y,'EdgeColor','none','LineStyle','none') % plot of entire image
    view(0, -90)
    

    编辑:格林威治标准时间 2015/05/18 15:45

    这具有剩余的强度值:

    img = im2double(imread('test.jpg'));
    rp = img(:, :, 1) / 255 ;
    bp = img(:, :, 2) / 255 ;
    gp = img(:, :, 3) / 255 ;
    kb = 0.114;
    kr = 0.299;
    x = kr * rp + (1 - kr - kb) * gp + kb * bp;
    x = max(max(x))-x;
    xp = x;
    xp(xp == min(min(xp))) = nan;
    
    y = x ./ x;
    
    z = y;
    z(isnan(y)) = 0;
    divisions = find(sum(z,2) > 5);
    divisions = [divisions(1); divisions(diff(divisions) > 10); size(z, 1)];
    
    rows = cell(length(divisions) - 1, 1);
    
    for i = 1:numel(rows)
        line = z(divisions(i):divisions(i+1), :);
        j = divisions(i) + find(sum(line,2) > 5) - 1;
        line = xp(j, :);
        rows{i} = line;
    
        figure(i) ; 
        surf(rows{i},'EdgeColor','none','LineStyle','none');
        axis('equal')
        view(0, -90) ;
    end
    
    figure(numel(rows) + 1)
    surf(xp,'EdgeColor','none','LineStyle','none')
    axis('equal')
    view(0, -90)
    

    编辑 2015-05-22 13:21 GMT

    %Turn warning message off
    warning('off', 'Images:initSize:adjustingMag');
    
    %Read in image in int8
    originalImg = imread('test.jpg');
    
    %Convert to double
    img = im2double(originalImg);
    
    %Take R, G, & B components
    rp = img(:, :, 1) ;
    gp = img(:, :, 2) ;
    bp = img(:, :, 3) ;
    
    %Get intensity
    kb = 0.114;
    kr = 0.299;
    yp = kr * rp + (1 - kr - kb) * gp + kb * bp;
    
    %Flip to opposite of intensity
    ypp = max(max(yp))-yp;
    
    %Normalize flipped intensity
    z = ypp ./ ypp;
    z(isnan(z)) = 0;
    
    %Find lines, this may need to be tuned
    MaxPixelsPerLine = 5;
    MinRowsPerLine = 10;
    divisions = find(sum(z,2) > MaxPixelsPerLine);
    divisions = [divisions(1); divisions(diff(divisions) > MinRowsPerLine); size(z, 1)];
    
    %Preallocate for number of lines
    colorRows = cell(length(divisions) - 1, 1);
    
    for i = 1:numel(rows)
        %Extract the lines in RGB
        line = z(divisions(i):divisions(i+1), :);
        j = divisions(i) + find(sum(line,2) > 5) - 1;
        colorRows{i} = originalImg(j, :, :);
    
        %Print out the line
        figure(i) ;
        imshow(colorRows{i})
    end
    
    %Print out the oringinal image
    figure(numel(rows) + 1)
    imshow(originalImg)
    
    %Turn the warning back on
    warning('on', 'Images:initSize:adjustingMag');
    

    【讨论】:

    • 你也可以尝试增加对比度。
    • 谢谢。这似乎工作得很好。但我不明白发生了什么。你能评论一下你的代码吗?此外,即使surf 单独绘制线条,有什么方法可以使用这些信息从原始图像中提取原始线条?谢谢
    • 各行存储在行中。我还放了一些 cmets。
    • 谢谢,这真的很有帮助。您的实现非常适合提取文本行,但问题是提取的行基本上以某种逻辑格式转换为 NaN(或 0)和 1,因此丢失了所有信息。有没有办法从原始图像中获取线条,即提取的线条是原始图像的颜色类型?甚至灰度也会,但不合逻辑。谢谢
    • 谢谢。但是值非常小,它们不再是原始格式。分割后有什么办法让他们回来?
    【解决方案2】:

    简短: graythresh(img) 可以解决您的问题

    更长:

    使用一些形态学方法,您可以很容易地提取线条。不过有个小缺点:它们有点乱。

    加载你的图片

    original = imread('o6WEN.jpg'); 
    

    把它变成灰度

    img=rgb2gray(original); .
    

    用大约 textheight 和 'very' long 定义一个矩形结构元素

    se = strel('rectangle',[30 200]); 
    

    用顶帽过滤器过滤它。在此之后,具有大约 textheight 的长矩形将更加突出。

     img = imtophat(img,se);
    

    调整对比度:

    img = imadjust(img);
    

    定义另一个结构元素,这次比 textheight 短一点:

    se = strel('line',20,0);

    用它放大图片以消除字母之间存在的空白

    img = imdilate(img,se);
    

    将图像变为黑色并带有:

    img=im2bw(img,graythresh(img));
    

    使用 regionprops 让所有 BoundingBoxes 形成你的线条

     stats=regionprops(img,'BoundingBox');
     figure, imshow(img)
    

    In Stats 现在是你所有行的边界框,遗憾的是乱序。也许这可以通过 BWlables 或某种相关性来纠正。 我只是查看了 BoundingBoxes 的 y 坐标并进行了相应的排序。

    BoundingBoxes=struct2cell(stats);
    BoundingBoxes=cell2mat(BoundingBoxes'); % making it into an array
    [~,ind]=sort(BoundingBoxes(:,2)); % sorting it to y
    BoundingBoxes=BoundingBoxes(ind,:); % apply the sorted vector 
    
     lineNr=8;
    imshow(original(BoundingBoxes(2,lineNr):BoundingBoxes(2,lineNr)+BoundingBoxes(4,lineNr),BoundingBoxes(1,lineNr):BoundingBoxes(1,lineNr)+BoundingBoxes(3,lineNr)  ))
    

    希望对你有用

    【讨论】:

    • 谢谢。你能评论一下lineNr的目的是什么吗?为什么设置为 8?同样在您的代码中,在imshow 的最后一行中给出了index exceeds matrix dimensions 的错误,谢谢
    • 最后两行只是显示某行,这里是第8行,演示如何获取单行的图像,可以删除。
    • 好的,谢谢。那么每条线的坐标在哪里呢?顺序对我来说并不重要,所以如果我要从原始图像中提取单独的行,我是否必须使用BoundingBoxes 中的数据?谢谢
    • 是的,数据在 BoundingBoxes 中:前两个值定义左下角,后两个值定义高度和宽度。
    猜你喜欢
    • 2018-10-25
    • 1970-01-01
    • 2017-06-29
    • 1970-01-01
    • 1970-01-01
    • 2019-08-10
    • 1970-01-01
    • 1970-01-01
    • 2019-04-14
    相关资源
    最近更新 更多