如何使用MATLAB使黑板文字看起来更清晰? [英] How to make the blackboard text appear clearer using MATLAB?

查看:36
本文介绍了如何使用MATLAB使黑板文字看起来更清晰?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我希望最终图像在数字类型外观上更清晰,我应该放置哪些过滤器序列.我的意思是只有两种不同的颜色,一种用于黑板,一种用于粉笔书写.

解决方案

在识别图像中的文本时,最好使用

的代码的 mex 实现<小时>

#include "mex.h"#include <向量>#include <地图>#include <设置>#include <算法>#include <math.h>使用命名空间标准;#define PI 3.14159265结构点2d {整数 x;输入 y;浮动 SWT;};struct Point2dFloat {浮动 x;浮动 y;};结构射线{Point2dp;Point2d q;std::vector积分;};void strokeWidthTransform(const float * edgeImage,const float * gradientX,const float * gradientY,布尔dark_on_light,浮动 * SWTImage,国际 h, 国际 w,std::vector<射线>&射线){//第一遍浮动 prec = .05f;for( int row = 0; row < h; row++ ){const float* ptr = edgeImage + row*w;for ( int col = 0; col < w; col++ ){如果 (*ptr > 0) {雷 r;Point2dp;p.x = 列;p.y = 行;r.p = p;std::vector积分;点.push_back(p);float curX = (float)col + 0.5f;float curY = (float)row + 0.5f;int curPixX = col;int curPixY = 行;浮动 G_x = gradientX[ col + row*w ];float G_y = gradientY[ col + row*w ];//标准化梯度浮动 mag = sqrt( (G_x * G_x) + (G_y * G_y) );如果(dark_on_light){G_x = -G_x/mag;G_y = -G_y/mag;} 别的 {G_x = G_x/mag;G_y = G_y/mag;}而(真){curX += G_x*prec;curY += G_y*prec;if ((int)(floor(curX)) != curPixX || (int)(floor(curY)) != curPixY) {curPixX = (int)(floor(curX));curPixY = (int)(floor(curY));//检查像素是否在图像边界外if (curPixX < 0 || (curPixX >= w) || curPixY < 0 || (curPixY >= h)) {休息;}Point2d pnew;pnew.x = curPixX;pnew.y = curPixY;点推回(pnew);if ( edgeImage[ curPixY*w+ curPixX ] > 0) {r.q = pnew;//点积浮动 G_xt = gradientX[ curPixY*w + curPixX ];float G_yt = gradientY[ curPixY*w + curPixX ];mag = sqrt( (G_xt * G_xt) + (G_yt * G_yt) );如果(dark_on_light){G_xt = -G_xt/mag;G_yt = -G_yt/mag;} 别的 {G_xt = G_xt/mag;G_yt = G_yt/mag;}如果 (acos(G_x * -G_xt + G_y * -G_yt) ::iterator pit = points.begin(); pit != points.end(); pit++) {float* pSWT = SWTImage + w * pit->y + pit->x;如果 (*pSWT <0) {*pSWT = 长度;} 别的 {*pSWT = std::min(length, *pSWT);}}r.points = 点数;射线.push_back(r);}休息;}}}}ptr++;}}}bool Point2dSort(const Point2d &lhs, const Point2d &rhs) {返回 lhs.SWT &射线,浮动 maxWidth = -1 ) {for (std::vector<Ray>::iterator rit = Ray.begin(); rit != Ray.end(); rit++) {for (std::vector::iterator pit = rit->points.begin(); pit != rit->points.end(); pit++) {坑-> SWT = SWTImage[w*pit->y + pit->x];}std::sort(rit->points.begin(), rit->points.end(), &Point2dSort);//std::nth_element( rit->points.begin(), rit->points.end(), rit->points.size()/2, &Point2dSort);浮动中位数 = (rit->points[rit->points.size()/2]).SWT;if ( maxWidth > 0 &&median >= maxWidth ) {中位数 = -1;}for (std::vector::iterator pit = rit->points.begin(); pit != rit->points.end(); pit++) {SWTImage[w*pit->y + pit->x] = std::min(pit->SWT,中值);}}}typedef std::vector>图_t;//图为每个节点的邻居列表void connComp( const graph_t& g, std::vector<int>& c, int i, int l ) {//从节点 i 开始用标签 l 标记这个 conn-compif ( i < 0 || i > g.size() ) {返回;}std::vector<国际 >堆;//推我stack.push_back(i);c[i] = l;而(!stack.empty()){//流行音乐i = stack.back();stack.pop_back();//遍历所有邻居for ( std::set::const_iterator it = g[i].begin(); it != g[i].end(); it++) {如果 ( c[*it] <0 ) {stack.push_back( *it );c[ *it ] = l;}}}}int findNextToLabel( const graph_t& g, const vector<int>& c ) {for ( int i = 0 ; i < c.size(); i++ ) {如果 ( c[i] <0 ) {返回我;}}返回 c.size();}int connected_components(const graph_t& g, vector<int>& c) {//检查空图!如果 ( g.empty() ) {返回0;}int i = 0;int num_conn = 0;做 {connComp( g, c, i, num_conn );num_conn++;i = findNextToLabel( g, c );} while ( i < g.size() );返回 num_conn;}std::vector>findLegallyConnectedComponents(const float* SWTImage, int h, int w,std::vector<射线>&射线){std::map地图;std::map修订版;std::vector>成分;//空的int num_vertices = 0, idx = 0;graph_t g;//图的顶点数.将每个点与数字关联for( int row = 0; row < h; row++ ){for (int col = 0; col < w; col++ ){idx = col + w * 行;如果(SWTImage[idx] > 0){地图[idx] = num_vertices;Point2dp;p.x = 列;p.y = 行;revmap[num_vertices] = p;num_vertices++;std::set空的;g.push_back(空);}}}如果 ( g.empty() ) {返回组件;//与空图无关...}for( int row = 0; row < h; row++ ){for (int col = 0; col < w; col++ ){idx = col + w * 行;如果(SWTImage[idx] > 0){//向右、右下、下、左下检查像素int this_pixel = Map[idx];浮动 thisVal = SWTImage[idx];如果 (col+1 < w) {右浮动 = SWTImage[ w*row + col + 1 ];if (right > 0 && (thisVal/right <= 3.0 || right/thisVal <= 3.0)) {g[this_pixel].insert( Map[ w*row + col + 1 ] );g[ Map[ w*row + col + 1 ] ].insert( this_pixel );//boost::add_edge(this_pixel, map.at(row * SWTImage->width + col + 1), g);}}如果(行 + 1  0 && (thisVal/right_down <= 3.0 || right_down/thisVal <= 3.0)) {g[ this_pixel ].insert( Map[ w*(row+1) + col + 1 ] );g[地图[w*(row+1)+col+1]].insert(this_pixel);//boost::add_edge(this_pixel, map.at((row+1) * SWTImage->width + col + 1), g);}}向下浮动 = SWTImage[ w*(row+1) + col ];if (down > 0 && (thisVal/down <= 3.0 || down/thisVal <= 3.0)) {g[ this_pixel ].insert( Map[ w*(row+1) + col ] );g[地图[w*(row+1)+col]].insert(this_pixel);//boost::add_edge(this_pixel, map.at((row+1) * SWTImage->width + col), g);}如果 (col-1 >= 0) {float left_down = SWTImage[ w*(row+1) + col - 1 ];if (left_down > 0 && (thisVal/left_down <= 3.0 || left_down/thisVal <= 3.0)) {g[ this_pixel ].insert( Map[ w*(row+1) + col - 1 ] );g[地图[w*(row+1)+col-1]].insert(this_pixel);//boost::add_edge(this_pixel, map.at((row+1) * SWTImage->width + col - 1), g);}}}}}}std::vectorc(num_vertices, -1);int num_comp = connected_components(g, c);components.reserve(num_comp);//std::cout <<在过滤之前,"<<num_comp <<" 组件和 " <<num_vertices<<顶点"<<std::endl;for (int j = 0; j tmp;components.push_back( tmp );}for (int j = 0; j  1, "只有一个输出");int h = mxGetN( pin[EIN]);//输入被转置!int w = mxGetM( pin[EIN]);mxAssert( mxIsClass( pin[EIN], mxSINGLE_CLASS ) && h == mxGetN( pin[EIN] ) && w == mxGetM( pin[EIN] ), "边缘图不正确");mxAssert( mxIsClass( pin[GXIN], mxSINGLE_CLASS ) && h == mxGetN( pin[GXIN] ) && w == mxGetM( pin[GXIN] ), "边缘图不正确");mxAssert( mxIsClass( pin[GYIN], mxSINGLE_CLASS ) && h == mxGetN( pin[GYIN] ) && w == mxGetM( pin[GYIN] ), "边缘图不正确");const float * edgeImage = (float*) mxGetData( pin[EIN]);const float * gradientX = (float*) mxGetData( pin[GXIN] );const float * gradientY = (float*) mxGetData( pin[GYIN] );bool dark_on_light = mxGetScalar( pin[DOLFIN]) != 0 ;float maxWidth = mxGetScalar( pin[MAXWIN]);//分配输出pout[0] = mxCreateNumericMatrix( w, h, mxSINGLE_CLASS, mxREAL);float * SWTImage = (float*) mxGetData( pout[0]);//设置 SWT 为 -1for ( int i = 0 ; i < w*h; i++ ) {SWTImage[i] = -1;}std::vector<射线>射线;strokeWidthTransform(edgeImage、gradientX、gradientY、dark_on_light、SWTImage、h、w、ray);SWTMedianFilter ( SWTImage, h, w, 光线, maxWidth );//连接组件如果(没有> 1){//从 SWT 和梯度图像计算合法连接组件.//返回类型是一个向量的向量,其中每个外部向量是一个组件,并且//内部向量包含该组件中每个像素的 (y,x).std::vector>组件 = findLegallyConnectedComponents(SWTImage, h, w, 射线);pout[1] = mxCreateNumericMatrix( w, h, mxSINGLE_CLASS, mxREAL);float* pComp = (float*) mxGetData( pout[1]);for ( int i = 0 ; i < w*h; i++ ) {pComp[i] = 0;}for ( int ci = 0 ; ci < components.size(); ci++ ) {for ( std::vector::iterator it = components[ci].begin() ; it != components[ci].end(); it++) {pComp[ w * it-> y + it-> x ] = ci + 1;}}}}

<小时>

Matlab 函数调用笔画宽度变换 (SWT) mex 文件:

function [swt swtcc] = SWT( img, dol, maxWidth )如果大小( img, 3 ) == 3img = rgb2gray(img);结尾img = im2single(img);edgeMap = single( edge( img, 'canny', .15 ) );img = imfilter( img, fspecial('gauss',[5 5], 0.3*(2.5-1)+.8));gx = imfilter( img, fspecial('prewitt')');%//'gy = imfilter(img, fspecial('prewitt'));gx = single(medfilt2( gx, [3 3] ));gy = single(medfilt2(gy, [3 3]));[swt swtcc] = swt_mex( edgeMap.', gx.', gy.', dol, maxWidth );%//'swt = swt';%//'swtcc = double(swtcc');%//'

What are the sequence of filters I should put if I want the final image to be more clearer with a digital type look. I mean only two distinct colors, one for the board and one for the chalk writing.

解决方案

When it comes to identifying text in images you better use Stroke Width Transform.

Here's a little result I obtained on your image (the basic transform + connected component w/o filtering):

My mex implementation based on code from here


#include "mex.h"
#include <vector>
#include <map>
#include <set>
#include <algorithm>
#include <math.h>
using namespace std;

#define PI 3.14159265

struct Point2d {
    int x;
    int y;
    float SWT;
};

struct Point2dFloat {
    float x;
    float y;
};

struct Ray {
    Point2d p;
    Point2d q;
    std::vector<Point2d> points;
};


void strokeWidthTransform(const float * edgeImage,
    const float * gradientX,
    const float * gradientY,
    bool dark_on_light,
    float * SWTImage,
    int h, int w,
    std::vector<Ray> & rays) {
    // First pass
    float prec = .05f;
    for( int row = 0; row < h; row++ ){
        const float* ptr = edgeImage + row*w;        
        for ( int col = 0; col < w; col++ ){
            if (*ptr > 0) {
                Ray r;

                Point2d p;
                p.x = col;
                p.y = row;
                r.p = p;
                std::vector<Point2d> points;
                points.push_back(p);

                float curX = (float)col + 0.5f;
                float curY = (float)row + 0.5f;
                int curPixX = col;
                int curPixY = row;
                float G_x = gradientX[ col + row*w ];                        
                float G_y = gradientY[ col + row*w ];
                // normalize gradient
                float mag = sqrt( (G_x * G_x) + (G_y * G_y) );
                if (dark_on_light){
                    G_x = -G_x/mag;
                    G_y = -G_y/mag;
                } else {
                    G_x = G_x/mag;
                    G_y = G_y/mag;                    
                }
                while (true) {
                    curX += G_x*prec;
                    curY += G_y*prec;
                    if ((int)(floor(curX)) != curPixX || (int)(floor(curY)) != curPixY)     {    
                        curPixX = (int)(floor(curX));
                        curPixY = (int)(floor(curY));
                        // check if pixel is outside boundary of image
                        if (curPixX < 0 || (curPixX >= w) || curPixY < 0 || (curPixY >= h)) {    
                            break;
                        }
                        Point2d pnew;
                        pnew.x = curPixX;
                        pnew.y = curPixY;
                        points.push_back(pnew);

                        if ( edgeImage[ curPixY*w+ curPixX ] > 0) {
                            r.q = pnew;
                            // dot product
                            float G_xt = gradientX[ curPixY*w + curPixX ];
                            float G_yt = gradientY[ curPixY*w + curPixX ];
                            mag = sqrt( (G_xt * G_xt) + (G_yt * G_yt) );
                            if (dark_on_light){
                                G_xt = -G_xt/mag;
                                G_yt = -G_yt/mag;
                            } else {
                                G_xt = G_xt/mag;
                                G_yt = G_yt/mag;                                
                            }

                            if (acos(G_x * -G_xt + G_y * -G_yt) < PI/2.0 ) {
                                float length = sqrt( ((float)r.q.x - (float)r.p.x)*((float)r.q.x - (float)r.p.x) + ((float)r.q.y - (float)r.p.y)*((float)r.q.y - (float)r.p.y));
                                for (std::vector<Point2d>::iterator pit = points.begin(); pit != points.end(); pit++) {
                                    float* pSWT = SWTImage +  w * pit->y + pit->x;
                                    if (*pSWT < 0) {
                                        *pSWT = length;
                                    } else {
                                        *pSWT = std::min(length, *pSWT);
                                    }
                                }
                                r.points = points;
                                rays.push_back(r);
                            }
                            break;
                        }
                    }
                }
            }
            ptr++;
        }
    }    
}


bool Point2dSort(const Point2d &lhs, const Point2d &rhs) {
    return lhs.SWT < rhs.SWT;
}

void SWTMedianFilter(float * SWTImage, int h, int w,
        std::vector<Ray> & rays, float maxWidth = -1 ) {
    for (std::vector<Ray>::iterator rit = rays.begin(); rit != rays.end(); rit++) {
        for (std::vector<Point2d>::iterator pit = rit->points.begin(); pit != rit->points.end(); pit++) {
            pit->SWT = SWTImage[ w*pit->y + pit->x ];
        }
        std::sort(rit->points.begin(), rit->points.end(), &Point2dSort);
        //std::nth_element( rit->points.begin(), rit->points.end(), rit->points.size()/2, &Point2dSort );
        float median = (rit->points[rit->points.size()/2]).SWT;
        if ( maxWidth > 0 && median >= maxWidth ) {
            median = -1;
        }
        for (std::vector<Point2d>::iterator pit = rit->points.begin(); pit != rit->points.end(); pit++) {
            SWTImage[ w*pit->y + pit->x ] = std::min(pit->SWT, median);
        }
    }    
}

typedef std::vector< std::set<int> > graph_t; // graph as a list of neighbors per node

void connComp( const graph_t& g, std::vector<int>& c, int i, int l ) {
    // starting from node i labe this conn-comp with label l
    if ( i < 0 || i > g.size() ) {
        return;
    }
    std::vector< int > stack;
    // push i
    stack.push_back(i);
    c[i] = l;
    while ( ! stack.empty() ) {
        // pop
        i = stack.back();
        stack.pop_back();
        // go over all nieghbors
        for ( std::set<int>::const_iterator it = g[i].begin(); it != g[i].end(); it++ ) {
            if ( c[*it] < 0 ) {
                stack.push_back( *it );
                c[ *it ] = l;
            }
        }
    }
}
int findNextToLabel( const graph_t& g, const vector<int>& c ) {
    for ( int i = 0 ; i < c.size(); i++ ) {
        if ( c[i] < 0 ) {
            return i;
        }
    }
    return c.size();
}

int connected_components(const graph_t& g, vector<int>& c) {
    // check for empty graph!
    if ( g.empty() ) {
        return 0;
    }
    int i = 0;
    int num_conn = 0;
    do {
        connComp( g, c, i, num_conn );
        num_conn++;
        i = findNextToLabel( g, c );
    } while ( i < g.size() );
    return num_conn;
}

std::vector< std::vector<Point2d> >
        findLegallyConnectedComponents(const float* SWTImage, int h, int w,
        std::vector<Ray> & rays) {
    std::map<int, int> Map;
    std::map<int, Point2d> revmap;
    std::vector<std::vector<Point2d> > components; // empty
    int num_vertices = 0, idx = 0;
    graph_t g;
    // Number vertices for graph.  Associate each point with number
    for( int row = 0; row < h; row++ ){        
        for (int col = 0; col < w; col++ ){
            idx = col + w * row;
            if (SWTImage[idx] > 0) {
                Map[idx] = num_vertices;
                Point2d p;
                p.x = col;
                p.y = row;
                revmap[num_vertices] = p;
                num_vertices++;
                std::set<int> empty;
                g.push_back(empty);
            }
        }
    }   
    if ( g.empty() ) {
        return components; // nothing to do with an empty graph...
    }
    for( int row = 0; row < h; row++ ){        
        for (int col = 0; col < w; col++ ){
            idx = col + w * row;
            if ( SWTImage[idx] > 0) {
                // check pixel to the right, right-down, down, left-down
                int this_pixel = Map[idx];
                float thisVal = SWTImage[idx];
                if (col+1 < w) {
                    float right = SWTImage[ w*row + col + 1 ];
                    if (right > 0 && (thisVal/right <= 3.0 || right/thisVal <= 3.0)) {
                        g[this_pixel].insert( Map[ w*row + col + 1 ] );                    
                        g[ Map[ w*row + col + 1 ] ].insert( this_pixel );
                        //boost::add_edge(this_pixel, map.at(row * SWTImage->width + col + 1), g);
                    }
                }
                if (row+1 < h) {
                    if (col+1 < w) {
                        float right_down = SWTImage[ w*(row+1) + col + 1 ];
                        if (right_down > 0 && (thisVal/right_down <= 3.0 || right_down/thisVal <= 3.0)) {
                            g[ this_pixel ].insert( Map[ w*(row+1) + col + 1 ] );
                            g[ Map[ w*(row+1) + col + 1 ] ].insert(this_pixel);                            
                            // boost::add_edge(this_pixel, map.at((row+1) * SWTImage->width + col + 1), g);
                        }
                    }
                    float down = SWTImage[ w*(row+1) + col ];
                    if (down > 0 && (thisVal/down <= 3.0 || down/thisVal <= 3.0)) {
                        g[ this_pixel ].insert( Map[ w*(row+1) + col ] );
                        g[ Map[ w*(row+1) + col ] ].insert( this_pixel );
                        //boost::add_edge(this_pixel, map.at((row+1) * SWTImage->width + col), g);
                    }
                    if (col-1 >= 0) {
                        float left_down = SWTImage[ w*(row+1) + col - 1 ];
                        if (left_down > 0 && (thisVal/left_down <= 3.0 || left_down/thisVal <= 3.0)) {
                            g[ this_pixel ].insert( Map[ w*(row+1) + col - 1 ] );
                            g[ Map[ w*(row+1) + col - 1 ] ].insert( this_pixel );
                            //boost::add_edge(this_pixel, map.at((row+1) * SWTImage->width + col - 1), g);
                        }
                    }
                }    
            }            
        }
    }

    std::vector<int> c(num_vertices, -1);    
    int num_comp = connected_components(g, c);    

    components.reserve(num_comp);
    //std::cout << "Before filtering, " << num_comp << " components and " <<     num_vertices << " vertices" << std::endl;
    for (int j = 0; j < num_comp; j++) {
        std::vector<Point2d> tmp;
        components.push_back( tmp );
    }
    for (int j = 0; j < num_vertices; j++) {
        Point2d p = revmap[j];
        (components[c[j]]).push_back(p);
    }

    return components;
}

enum {
    EIN = 0,
    GXIN,
    GYIN,
    DOLFIN,
    MAXWIN,
    NIN };

void mexFunction( int nout, mxArray* pout[], int nin, const mxArray* pin[] ) {
    //    
    // make sure images are input in transposed so that they are arranged row-major in memory
    //
    mxAssert( nin == NIN, "wrong number of inputs" );
    mxAssert( nout > 1, "only one output" );

    int h = mxGetN( pin[EIN] ); // inputs are transposed!
    int w = mxGetM( pin[EIN] );

    mxAssert( mxIsClass( pin[EIN], mxSINGLE_CLASS ) && h == mxGetN( pin[EIN] ) && w == mxGetM( pin[EIN] ), "edge map incorrect");
    mxAssert( mxIsClass( pin[GXIN], mxSINGLE_CLASS ) && h == mxGetN( pin[GXIN] ) && w == mxGetM( pin[GXIN] ), "edge map incorrect");
    mxAssert( mxIsClass( pin[GYIN], mxSINGLE_CLASS ) && h == mxGetN( pin[GYIN] ) && w == mxGetM( pin[GYIN] ), "edge map incorrect");

    const float * edgeImage = (float*) mxGetData( pin[EIN] );
    const float * gradientX = (float*) mxGetData( pin[GXIN] );
    const float * gradientY = (float*) mxGetData( pin[GYIN] );

    bool dark_on_light = mxGetScalar( pin[DOLFIN] ) != 0 ;
    float maxWidth = mxGetScalar( pin[MAXWIN] );

    // allocate output
    pout[0] = mxCreateNumericMatrix( w, h, mxSINGLE_CLASS, mxREAL );
    float * SWTImage = (float*) mxGetData( pout[0] );
    // set SWT to -1
    for ( int i = 0 ; i < w*h; i++ ) {
        SWTImage[i] = -1;
    }

    std::vector<Ray> rays;
    strokeWidthTransform ( edgeImage, gradientX, gradientY, dark_on_light, SWTImage, h, w, rays );
    SWTMedianFilter ( SWTImage, h, w, rays, maxWidth );

    // connected components
    if ( nout > 1 ) {
        // Calculate legally connect components from SWT and gradient image.
        // return type is a vector of vectors, where each outer vector is a component and
        // the inner vector contains the (y,x) of each pixel in that component.
        std::vector<std::vector<Point2d> > components = findLegallyConnectedComponents(SWTImage, h, w, rays);
        pout[1] = mxCreateNumericMatrix( w, h, mxSINGLE_CLASS, mxREAL );
        float* pComp = (float*) mxGetData( pout[1] );
        for ( int i = 0 ; i < w*h; i++ ) {
            pComp[i] = 0;
        }
        for ( int ci = 0 ; ci < components.size(); ci++ ) {
            for ( std::vector<Point2d>::iterator it = components[ci].begin() ; it != components[ci].end(); it++ ) {
                pComp[ w * it->y + it->x ] = ci + 1;
            }
        }
    }
}


Matlab function calling stroke-width-transform (SWT) mex-file:

function [swt swtcc] = SWT( img, dol, maxWidth )

if size( img, 3 ) == 3
    img = rgb2gray(img);
end
img = im2single(img);

edgeMap = single( edge( img, 'canny', .15 ) ); 
img = imfilter( img, fspecial('gauss',[5 5], 0.3*(2.5-1)+.8) );
gx = imfilter( img, fspecial('prewitt')' ); %//'
gy = imfilter( img, fspecial('prewitt') );
gx = single(medfilt2( gx, [3 3] ));
gy = single(medfilt2( gy, [3 3] ));

[swt swtcc] = swt_mex( edgeMap.', gx.', gy.', dol, maxWidth ); %//'

swt = swt'; %//'
swtcc = double(swtcc'); %//'

这篇关于如何使用MATLAB使黑板文字看起来更清晰?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆