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

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

问题描述

如果我希望最终图像更清晰,具有数字类型的外观,我应该放置什么样的滤镜序列.我的意思是只有两种不同的颜色,一种用于白板,一种用于粉笔书写.

解决方案

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

中的代码<小时>

#include "mex.h"#include <向量>#include <地图>#include <设置>#include <算法>#include <math.h>使用命名空间标准;#define PI 3.14159265结构 Point2d {诠释 x;整数y;浮动 SWT;};结构 Point2dFloat {浮动 x;浮动y;};结构射线{Point2d p;Point2d q;std::vector<Point2d>积分;};void strokeWidthTransform(const float * edgeImage,常量浮动*梯度X,常量浮动*梯度Y,布尔暗开光,浮动 * SWTImage,整数 h,整数 w,std::vector<Ray>&射线){//第一关浮点数 = .05f;for(int row = 0; row < h; row++ ){常量浮动* ptr = 边缘图像 + 行*w;for (int col = 0; col < w; col++){如果 (*ptr > 0) {雷 r;Point2d p;p.x = col;p.y = 行;r.p = p;std::vector<Point2d>积分;点.push_back(p);浮动 curX = (float)col + 0.5f;浮动 curY = (float)row + 0.5f;int curPixX = col;int curPixY = 行;浮动 G_x = gradientX[ col + row*w ];浮动 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;如果 ((int)(floor(curX)) != curPixX || (int)(floor(curY)) != curPixY) {curPixX = (int)(地板(curX));curPixY = (int)(地板(curY));//检查像素是否在图像边界之外if (curPixX <0 || (curPixX >= w) || curPixY <0 || (curPixY >= h)) {休息;}Point2d pnew;pnew.x = curPixX;pnew.y = curPixY;points.push_back(pnew);if (edgeImage[curPixY*w+curPixX]>0){r.q = pnew;//点积浮动 G_xt = gradientX[ curPixY*w + curPixX ];浮动 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;}if (acos(G_x * -G_xt + G_y * -G_yt) < PI/2.0 ) {浮点长度 = sqrt( ((float)rqx - (float)rpx)*((float)rqx - (float)rpx) + ((float)rqy - (float)rpy)*((float)rqy - (float)rpy));for (std::vector::iterator pit = points.begin();pit != points.end();pit++) {浮动* pSWT = SWTImage + w * 坑->y + 坑->x;if (*pSWT <0) {*pSWT = 长度;} 别的 {*pSWT = std::min(长度, *pSWT);}}r.points = 点数;射线.push_back(r);}休息;}}}}指针++;}}}bool Point2dSort(const Point2d &lhs, const Point2d &rhs) {返回 lhs.SWT ::iterator pit = rit->points.begin();pit != rit->points.end();pit++) {坑->SWT = SWTImage[w*pit->y + 坑->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 && 中值 >= 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>组件;//空的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 * 行;if (SWTImage[idx] > 0) {地图[idx] = num_vertices;Point2d p;p.x = col;p.y = 行;revmap[num_vertices] = p;num_vertices++;std::set<int>空的;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 = 地图[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(地图[w*row + col + 1]);g[ 地图[ w*row + col + 1 ] ].insert( this_pixel );//boost::add_edge(this_pixel, map.at(row * SWTImage->width + col + 1), g);}}如果(行+1 < h){如果 (col+1 < w) {浮动 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( 地图[ 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( 地图[ 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) {浮动 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( 地图[ 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::vector<int>c(num_vertices, -1);int num_comp = connected_components(g, c);components.reserve(num_comp);//std::cout <<过滤前," <<num_comp <<组件和" <<num_vertices <<顶点"<<标准::endl;for (int j = 0; j < num_comp; j++) {std::vector<Point2d>时间;components.push_back(tmp);}for (int j = 0; j < num_vertices; j++) {Point2d p = revmap[j];(组件[c[j]]).push_back(p);}返回组件;}枚举{EIN = 0,鑫鑫,银,海豚,麦克斯温,宁};无效 mexFunction(int nout,mxArray* pout[],int nin,const mxArray* pin[]){////确保图像以转置方式输入,以便它们在内存中以行为主//mxAssert(nin == NIN, "输入数错误");mxAssert(nout > 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 ;浮动 maxWidth = mxGetScalar( pin[MAXWIN] );//分配输出pout[0] = mxCreateNumericMatrix(w, h, mxSINGLE_CLASS, mxREAL);浮动 * SWTImage = (float*) mxGetData( pout[0] );//将 SWT 设置为 -1for ( int i = 0 ; i < w*h; i++ ) {SWTImage[i] = -1;}std::vector<Ray>射线;strokeWidthTransform (edgeImage, gradientX, gradientY, dark_on_light, SWTImage, h, w, 光线);SWTMedianFilter ( SWTImage, h, w, 光线, maxWidth );//连接组件if (nout > 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 函数调用 stroke-width-transform (SWT) mex-file:

函数 [swt swtcc] = SWT(img, dol, maxWidth)如果大小(img,3)== 3img = rgb2gray(img);结尾img = im2single(img);edgeMap = single(edge(img, 'canny', .15) );img = imfilter(img, fspecial('高斯',[5 5], 0.3*(2.5-1)+.8) );gx = imfilter(img, fspecial('prewitt')' );%//'gy = imfilter(img, fspecial('prewitt') );gx =单(medfilt2(gx,[3 3]));gy =单(medfilt2(gy,[3 3]));[swt swtcc] = swt_mex(edgeMap.', gx.', gy.', dol, maxWidth);%//'swt = swt';%//'swtcc = 双(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天全站免登陆