Рукописный ввод - распознавание в стиле граффити
От: What Беларусь  
Дата: 21.03.05 00:08
Оценка: 17 (3)
Доброго времени суток.
Когда-то для лабораторной по ИИ придумал алгоритм распознавания рукописного ввода и реализовал его в виде аплета на джаве.
Здесь можно с ним поиграться on-line (нужна Java).
Алгоритм распознавания описан в другой ветке
Автор: What
Дата: 18.03.05

Здесь лежат исходники "распознавателя" и редактора символов на C++.
А здесь лежат бинарные файлы "распознавателя" и редактора символов на C++ (для работы нужны mfc71.dll, msvcp71.dll, msvcr71.dll).

А вот код примера (см. ссылку выше) на Java.
/*
ReadMe.java
version 1.0.1, March 21, 2005

Copyright (C) 2004-2005 Yury Melnichek

This software is provided 'as-is', without any express or implied
warranty.  In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
   claim that you wrote the original software. If you use this software
   in a product, an acknowledgment in the product documentation would be
   appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
   misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

Yury Melnichek
melnichek (at) gmail (dot) com
*/

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Point2D;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.io.*;
import java.net.URL;

class Symbol
{
    static class FileFormatException extends Exception
    {
        public FileFormatException(String s)
        {
            super(s);
        }
    }
    
    private static class Diff
    {
        Diff(double d, double alpha)
        {
            this.d = d;
            this.alpha = alpha;
        }

        /** Расстояние по кривой. 0 <= d <= 1. */
        public double d;
        /** Угол наклона касательной. 0 <= alpha < 2*PI */
        public double alpha;
    }

    private List points;
    private LinkedList diffs;
    private String character;
    public static double MAX_DISTANCE = Double.MAX_VALUE;

    public Symbol(List points)
    {
        this.points = points;
        this.character = null;
        preCalc();
    }

    public Symbol(List points, String c)
    {
        this.points = points;
        this.character = c;
        preCalc();
    }

    public Symbol(int[][] Data, String c)
    {
        this.character = c;
        LinkedList l = new LinkedList();
        for (int i = 0; i < Data.length; ++i)
        {
            l.addLast(new Point(Data[i][0], Data[i][1]));
        }
        this.points = l;
        preCalc();
    }

    private void preCalc()
    {
        this.diffs = new LinkedList();
        ListIterator i = this.points.listIterator();
        Point2D prevPt = (Point2D) i.next();
        double sum = 0.0;
        while (i.hasNext())
        {
            Point2D pt = (Point2D) i.next();
            sum += pt.distance(prevPt);
            double alpha = StrictMath.atan2(pt.getY() - prevPt.getY(), pt
                    .getX()
                    - prevPt.getX());
            if (alpha < 0.0)
                alpha += 2.0 * StrictMath.PI;
            this.diffs.add(new Diff(sum, alpha));
            prevPt = pt;
        }
        ListIterator j = this.diffs.listIterator();
        while (j.hasNext())
            ((Diff) j.next()).d /= sum;
    }

    /** @return Разница углов на отрезке [0; PI]. */
    static private double deltaAngles(double alpha1, double alpha2)
    {
        double dalpha = StrictMath.abs(alpha1 - alpha2);
        if (dalpha > StrictMath.PI)
            dalpha = 2.0 * StrictMath.PI - dalpha;
        return dalpha;
    }

    public double distance(Symbol s)
    {
        ListIterator i1 = this.diffs.listIterator();
        ListIterator i2 = s.diffs.listIterator();
        Diff d1 = (Diff) i1.next();
        Diff d2 = (Diff) i2.next();
        double dist = 0.0;
        double dcur = StrictMath.min(d1.d, d2.d);
        double dOld = 0.0;
        while (dcur < 1.0)
        {
            dist += deltaAngles(d1.alpha, d2.alpha) * (dcur - dOld);
            if (dcur == 1.0)
                break;
            if (d1.d <= dcur)
                d1 = (Diff) i1.next();
            if (d2.d <= dcur)
                d2 = (Diff) i2.next();
            dOld = dcur;
            dcur = StrictMath.min(d1.d, d2.d);
        }
        System.out.println("distance to \'" + this.character + "\': " + dist);
        return dist;
    }

    public String getCharacter()
    {
        return this.character;
    }

    public static void readSymbols(InputStream is, List l)
            throws Symbol.FileFormatException, java.io.IOException
    {
        StreamTokenizer st = new StreamTokenizer(new BufferedReader(
                new InputStreamReader(is)));
        st.eolIsSignificant(true);
        while (st.nextToken() != StreamTokenizer.TT_EOF)
        {
            if (st.ttype == StreamTokenizer.TT_EOL)
                continue;
            if (st.sval != null)
            {
                LinkedList points = new LinkedList();
                String name = st.sval;
                if (st.nextToken() != StreamTokenizer.TT_NUMBER)
                    throw new Symbol.FileFormatException("Number expected: "
                            + st.toString());
                do
                {
                    double x = st.nval;
                    if (st.nextToken() != StreamTokenizer.TT_NUMBER)
                        throw new Symbol.FileFormatException(
                                "Number expected: " + st.toString());
                    double y = st.nval;
                    points.add(new Point2D.Double(x, y));
                }
                while (st.nextToken() == StreamTokenizer.TT_NUMBER);
                l.add(new Symbol(points, name));
                continue;
            }
            throw new Symbol.FileFormatException("Unknown token: "
                    + st.toString());
        }
    }
}

class InputSymbol implements MouseListener, MouseMotionListener
{
    static interface InputHandler
    {
        public void processSymbolInput(LinkedList points);
    }

    InputSymbol(Graphics graphics, InputHandler inputHandler)
    {
        this.graphics = graphics;
        this.inputHandler = inputHandler;
        this.points = null;
    }

    public void mouseClicked(MouseEvent e)
    {
    }

    public void mousePressed(MouseEvent e)
    {
        startCurve(e.getPoint());
    }

    public void mouseReleased(MouseEvent e)
    {
        stopCurve();
    }

    public void mouseEntered(MouseEvent e)
    {
    }

    public void mouseExited(MouseEvent e)
    {
        stopCurve();
    }

    public void mouseDragged(MouseEvent e)
    {
        addPoint(e.getPoint());
    }

    public void mouseMoved(MouseEvent e)
    {
    }

    private void startCurve(Point point)
    {
        this.points = new LinkedList();
        this.points.add(point);
    }

    private void stopCurve()
    {
        if ((this.points != null) && this.points.size() > 1)
        {
            this.inputHandler.processSymbolInput(this.points);
            this.points = null;
        }
    }

    private void addPoint(Point point)
    {
        if (this.points == null) return;
        Point curPt = (Point)this.points.getLast();
        this.graphics.drawLine(curPt.x, curPt.y, point.x, point.y);
        this.points.addLast(point);
    }

    LinkedList points;
    InputHandler inputHandler;
    Graphics graphics;
}

class InputArea extends Canvas
{
    public static interface SymbolInputListener
    {
        public void SymbolEntered(Symbol symbol, double distance);
    }

    LinkedList listeners = new LinkedList();
    LinkedList symbols;
    
    public void addSymbolInputListener(SymbolInputListener listener)
    {
        this.listeners.addLast(listener);
    }
    
    public void removeSymbolInputListener(SymbolInputListener listener)
    {
        this.listeners.remove(listener);
    }


    class InputHandler implements InputSymbol.InputHandler
    {
        public void processSymbolInput(LinkedList points)
        {
            System.out.println();
            Symbol s = new Symbol(points);
            Symbol best = null;
            double mindist = Symbol.MAX_DISTANCE;
            ListIterator i = InputArea.this.symbols.listIterator();
            while (i.hasNext())
            {
                Symbol cur = (Symbol)i.next();
                double dist = cur.distance(s);
                if (dist < mindist)
                {
                    mindist = dist;
                    best = cur;
                }
            }
            if (best != null)
            {
                ListIterator j = InputArea.this.listeners.listIterator();
                while (j.hasNext()) ((SymbolInputListener)j.next()).SymbolEntered(best, mindist);
            }
            update(getGraphics());
        }
    }


    public InputArea(LinkedList symbols)
    {
        this.symbols = symbols;
    }

    public void initialize()
    {
        InputSymbol is = new InputSymbol(getGraphics(), new InputHandler());
        addMouseMotionListener(is);
        addMouseListener(is);
    }

    public Dimension getPreferredSize()
    {
        return new Dimension(10000, 10000);
    }

    public Dimension getMinimumSize()
    {
        return new Dimension(64, 64);
    }
}

public class ReadMe extends Applet implements InputArea.SymbolInputListener
{
    private Color getColorParameter(String s)
    {
        try
        {
            String str = getParameter(s);
            if (str != null)
            {
                Color color = Color.decode(str);
                return color;
            }
        }
        catch (NumberFormatException e)
        {
        }
        return null;
    }

    InputArea inputArea;
    TextField textField;

    public void init()
    {
        this.textField = new TextField();
        setLayout(new BorderLayout());
        Color color;
        if ((color = getColorParameter("TextBackgroundColor")) != null)
            this.textField.setBackground(color);
        if ((color = getColorParameter("TextForegroundColor")) != null)
            this.textField.setForeground(color);
        add(this.textField, BorderLayout.SOUTH);
        InputStream is = null;
        try
        {
            String sDataFile = getParameter("Data");
            is = new URL(getDocumentBase(), sDataFile).openStream();
            LinkedList symlist = new LinkedList();
            Symbol.readSymbols(is, symlist);
            this.inputArea = new InputArea(symlist);
            this.inputArea.addSymbolInputListener(this);
            if ((color = getColorParameter("AreaBackgroundColor")) != null)
                this.inputArea.setBackground(color);
            if ((color = getColorParameter("AreaForegroundColor")) != null)
                this.inputArea.setForeground(color);
            add(this.inputArea, BorderLayout.NORTH);
            validate();
            this.inputArea.initialize();
        }
        catch (Exception e)
        {
            System.out.println(e.getMessage());
            return;
        }
    }

    public String getAppletInfo()
    {
        return "ReadMe applet. Copyright (C) 2004-2005 Yury Melnichek <melnichek (at) gmail (dot) com>";
    }

    public void SymbolEntered(Symbol symbol, double distance)
    {
        this.textField.setText(this.textField.getText() + symbol.getCharacter());
    }
}
Re: Рукописный ввод - распознавание в стиле граффити
От: Chamele0n  
Дата: 21.03.05 00:26
Оценка:
Здравствуйте, What, Вы писали:

Чего то у меня только нули и четверки получаются =)
... << RSDN@Home 1.1.3 stable >>
Re[2]: Рукописный ввод - распознавание в стиле граффити
От: What Беларусь  
Дата: 21.03.05 07:01
Оценка:
Здравствуйте, Chamele0n, Вы писали:

C>Чего то у меня только нули и четверки получаются =)

Не знаю. Сам только что пробовал — работают все цифры.
Хинт: сверху на картинке — правила рисования.
Нажимаем левую кнопку мыши, рисуем, отпускаем левую кнопку мыши.
Начинать рисовать надо с места, помеченного точкой.
... << RSDN@Home 1.1.4 beta 4 rev. 302>>
Re[3]: Рукописный ввод - распознавание в стиле граффити
От: Chamele0n  
Дата: 21.03.05 08:30
Оценка:
Здравствуйте, What, Вы писали:

Так это программа по обучению человека писать получается =)
... << RSDN@Home 1.1.3 stable >>
Re[4]: Рукописный ввод - распознавание в стиле граффити
От: What Беларусь  
Дата: 21.03.05 08:34
Оценка:
Здравствуйте, Chamele0n, Вы писали:

C>Так это программа по обучению человека писать получается =)

Так написание можно настроить под себя
... << RSDN@Home 1.1.4 beta 4 rev. 302>>
Re: Рукописный ввод - распознавание в стиле граффити
От: Аноним  
Дата: 22.03.05 02:51
Оценка:
Здорово,

но как распозновать если рисуется не один символ а несколько....
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.