ব্যবহারকারীর স্পর্শ থেকে একটি নিখুঁত বৃত্ত আঁকুন


176

আমার কাছে এই অনুশীলন প্রকল্প রয়েছে যা ব্যবহারকারীরা তাদের আঙ্গুলগুলির সাথে স্পর্শ করার সাথে সাথে স্ক্রিনে আঁকতে সক্ষম করে। ব্যায়াম হিসাবে ফিরে আসার মতো খুব সাধারণ অ্যাপ্লিকেশনটি। আমার ছোট মামাতো ভাই এই অ্যাপটিতে আইপ্যাড দিয়ে আঙুল দিয়ে জিনিস আঁকার স্বাধীনতা নিয়েছিল (বাচ্চাদের আঁকাগুলি: বৃত্ত, লাইন ইত্যাদি, যা তার মনে আসে)। তারপরে তিনি চেনাশোনাগুলি আঁকতে শুরু করলেন এবং তারপরে তিনি আমাকে এটিকে "ভাল বৃত্ত" তৈরি করতে বললেন (আমার বুদ্ধি থেকে: টানা বৃত্তটিকে পুরোপুরি গোল করে দিন, কারণ আমরা জানি না যতই স্থির থাকি আমরা পর্দায় আঙুল দিয়ে কিছু আঁকতে চেষ্টা করি না, চেনাশোনা কখনই বৃত্তের মতো গোলাকার হয় না)

সুতরাং এখানে আমার প্রশ্নটি হ'ল, কোডের কোনও উপায় আছে যেখানে আমরা প্রথমে ব্যবহারকারীর দ্বারা আঁকানো একটি লাইন সনাক্ত করতে পারি যা একটি বৃত্ত তৈরি করে এবং পর্দার পুরোপুরি বৃত্তাকার করে বৃত্তের প্রায় একই আকারের উত্পাদন করতে পারে। খুব সরলরেখাকে সোজা না করা এমন কিছু যা আমি করতাম কীভাবে তা জানতাম তবে বৃত্তাকার হিসাবে, কোয়ার্টজ বা অন্যান্য পদ্ধতিতে এটি কীভাবে করা যায় তা আমি বেশিরভাগই জানি না।

আমার যুক্তিটি হ'ল, লাইনটির শুরু এবং শেষের পয়েন্টটি অবশ্যই একে অপরকে স্পর্শ বা অতিক্রম করতে হবে যখন ব্যবহারকারী আঙুলটি উত্থাপন করে সত্যটি প্রমাণ করতে যে তিনি আসলে একটি বৃত্ত আঁকতে চেষ্টা করেছিলেন।


2
এই দৃশ্যে একটি বৃত্ত এবং বহুভুজের মধ্যে পার্থক্যটি বলা শক্ত হতে পারে। কীভাবে একটি "সার্কেল সরঞ্জাম" থাকার বিষয়ে যেখানে ব্যবহারকারী কেন্দ্র বা একটি বাউন্ডিং আয়তক্ষেত্রের একটি কোণকে সংজ্ঞায়িত করতে ক্লিক করে এবং ব্যাসার্ধ পরিবর্তন করতে বা বিপরীত কোণটি সেট করতে ক্লিক করেন?
ব্যবহারকারী 1118321

2
@ ব্যবহারকারী 1118321: এটি কেবল একটি বৃত্ত আঁকতে সক্ষম হওয়ার এবং একটি নিখুঁত বৃত্ত ধারণার ধারণাটিকে পরাস্ত করে। আদর্শভাবে, ব্যবহারকারীর কোনও বৃত্ত (কম বা কম), উপবৃত্তাকার বা বহুভুজ অঙ্কিত কিনা তা অ্যাপ্লিকেশনটিকে একাই ব্যবহারকারীর অঙ্কন থেকে স্বীকৃত করা উচিত। (প্লাস, বহুভুজগুলি এই অ্যাপ্লিকেশনটির পক্ষে নাও থাকতে পারে - এটি কেবল চেনাশোনা বা লাইন হতে পারে))
পিটার হোসি

সুতরাং, আপনি কোন উত্তরে আমার মনে হয় আমাকে অনুগ্রহ দেওয়া উচিত? আমি অনেক ভাল প্রার্থী দেখতে পাচ্ছি।
পিটার হোসি

@ উনিহিলিগ: ট্রিগ সম্পর্কে একটি নবীন বোধের বাইরে এই বিষয়ে আমার কোনও দক্ষতা নেই। এটি বলেছিল, যে উত্তরগুলি আমার কাছে সর্বাধিক সম্ভাবনাকে দেখায় তা হ'ল স্ট্যাকওভারফ্লো. com / a / 19071980/ 30461 , স্ট্যাকওভারফ্লো. com / a / 19055873/ 30461 , স্ট্যাকওভারফ্লো. com / a/ 18995771 / 30461 , সম্ভবত স্ট্যাকওভারফ্লো. com / a/ 18992200/30461 , এবং আমার নিজের। এগুলিই আমি প্রথমে চেষ্টা করব। আমি আদেশ আপনাকে ছেড়ে।
পিটার হোসে 19

1
@ জিন: সম্ভবত আপনি কোনও উত্তরটিতে প্রাসঙ্গিক তথ্যগুলি এবং আরও বিশদে লিঙ্ক করতে পারেন to
পিটার হোসি

উত্তর:


381

কখনও কখনও চাকা পুনরুদ্ধার করতে কিছু সময় ব্যয় করা সত্যই দরকারী। আপনি ইতিমধ্যে লক্ষ করেছেন যে এখানে প্রচুর ফ্রেমওয়ার্ক রয়েছে তবে এত জটিলতার পরিচয় না দিয়ে কোনও সহজ, তবে কার্যকর সমাধান কার্যকর করা খুব কঠিন নয়। (দয়া করে আমাকে ভুল করবেন না, যে কোনও গুরুতর উদ্দেশ্যে কিছু পরিপক্ক এবং স্থিতিশীল কাঠামো হিসাবে প্রমাণিত হওয়া ভাল)

আমি প্রথমে আমার ফলাফলগুলি উপস্থাপন করব এবং তারপরে তাদের পেছনের সহজ এবং সরল ধারণাটি ব্যাখ্যা করব।

এখানে চিত্র বর্ণনা লিখুন

আপনি আমার বাস্তবায়নে দেখবেন প্রতি একক পয়েন্ট বিশ্লেষণ করার এবং জটিল গণনা করার দরকার নেই। ধারণাটি কিছু মূল্যবান মেটা তথ্য স্পট করা হয়। আমি উদাহরণ হিসাবে স্পর্শক ব্যবহার করব :

এখানে চিত্র বর্ণনা লিখুন

আসুন একটি সরল এবং সোজা প্যাটার্ন চিহ্নিত করুন, নির্বাচিত আকারের জন্য আদর্শ:

এখানে চিত্র বর্ণনা লিখুন

সুতরাং সেই ধারণার উপর ভিত্তি করে একটি চেনাশোনা সনাক্তকরণ প্রক্রিয়াটি প্রয়োগ করা এত কঠিন নয়। নীচে কাজের ডেমো দেখুন (দুঃখিত, আমি এই দ্রুত এবং কিছুটা নোংরা উদাহরণ প্রদানের দ্রুততম উপায় হিসাবে জাভা ব্যবহার করছি):

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.HeadlessException;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class CircleGestureDemo extends JFrame implements MouseListener, MouseMotionListener {

    enum Type {
        RIGHT_DOWN,
        LEFT_DOWN,
        LEFT_UP,
        RIGHT_UP,
        UNDEFINED
    }

    private static final Type[] circleShape = {
        Type.RIGHT_DOWN,
        Type.LEFT_DOWN,
        Type.LEFT_UP,
        Type.RIGHT_UP};

    private boolean editing = false;
    private Point[] bounds;
    private Point last = new Point(0, 0);
    private List<Point> points = new ArrayList<>();

    public CircleGestureDemo() throws HeadlessException {
        super("Detect Circle");

        addMouseListener(this);
        addMouseMotionListener(this);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        setPreferredSize(new Dimension(800, 600));
        pack();
    }

    @Override
    public void paint(Graphics graphics) {
        Dimension d = getSize();
        Graphics2D g = (Graphics2D) graphics;

        super.paint(g);

        RenderingHints qualityHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        qualityHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g.setRenderingHints(qualityHints);

        g.setColor(Color.RED);
        if (cD == 0) {
            Point b = null;
            for (Point e : points) {
                if (null != b) {
                    g.drawLine(b.x, b.y, e.x, e.y);
                }
                b = e;
            }
        }else if (cD > 0){
            g.setColor(Color.BLUE);
            g.setStroke(new BasicStroke(3));
            g.drawOval(cX, cY, cD, cD);
        }else{
            g.drawString("Uknown",30,50);
        }
    }


    private Type getType(int dx, int dy) {
        Type result = Type.UNDEFINED;

        if (dx > 0 && dy < 0) {
            result = Type.RIGHT_DOWN;
        } else if (dx < 0 && dy < 0) {
            result = Type.LEFT_DOWN;
        } else if (dx < 0 && dy > 0) {
            result = Type.LEFT_UP;
        } else if (dx > 0 && dy > 0) {
            result = Type.RIGHT_UP;
        }

        return result;
    }

    private boolean isCircle(List<Point> points) {
        boolean result = false;
        Type[] shape = circleShape;
        Type[] detected = new Type[shape.length];
        bounds = new Point[shape.length];

        final int STEP = 5;

        int index = 0;        
        Point current = points.get(0);
        Type type = null;

        for (int i = STEP; i < points.size(); i += STEP) {
            Point next = points.get(i);
            int dx = next.x - current.x;
            int dy = -(next.y - current.y);

            if(dx == 0 || dy == 0) {
                continue;
            }

            Type newType = getType(dx, dy);
            if(type == null || type != newType) {
                if(newType != shape[index]) {
                    break;
                }
                bounds[index] = current;
                detected[index++] = newType;
            }
            type = newType;            
            current = next;

            if (index >= shape.length) {
                result = true;
                break;
            }
        }

        return result;
    }

    @Override
    public void mousePressed(MouseEvent e) {
        cD = 0;
        points.clear();
        editing = true;
    }

    private int cX;
    private int cY;
    private int cD;

    @Override
    public void mouseReleased(MouseEvent e) {
        editing = false;
        if(points.size() > 0) {
            if(isCircle(points)) {
                cX = bounds[0].x + Math.abs((bounds[2].x - bounds[0].x)/2);
                cY = bounds[0].y;
                cD = bounds[2].y - bounds[0].y;
                cX = cX - cD/2;

                System.out.println("circle");
            }else{
                cD = -1;
                System.out.println("unknown");
            }
            repaint();
        }
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        Point newPoint = e.getPoint();
        if (editing && !last.equals(newPoint)) {
            points.add(newPoint);
            last = newPoint;
            repaint();
        }
    }

    @Override
    public void mouseMoved(MouseEvent e) {
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }

    @Override
    public void mouseClicked(MouseEvent e) {
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                CircleGestureDemo t = new CircleGestureDemo();
                t.setVisible(true);
            }
        });
    }
}

আইওএস-এ একইরকম আচরণ বাস্তবায়নে সমস্যা হওয়া উচিত নয়, যেহেতু আপনার কেবল কয়েকটি ইভেন্ট এবং সমন্বয় প্রয়োজন। নীচের মতো কিছু ( উদাহরণ দেখুন ):

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch* touch = [[event allTouches] anyObject];
}

- (void)handleTouch:(UIEvent *)event {
    UITouch* touch = [[event allTouches] anyObject];
    CGPoint location = [touch locationInView:self];

}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    [self handleTouch: event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [self handleTouch: event];    
}

বেশ কয়েকটি বর্ধন সম্ভব।

যে কোনও মুহুর্তে শুরু করুন

নিম্নোক্ত সরলকরণের কারণে শীর্ষের মধ্যবিন্দু থেকে একটি বৃত্ত অঙ্কন শুরু করা বর্তমান প্রয়োজন:

        if(type == null || type != newType) {
            if(newType != shape[index]) {
                break;
            }
            bounds[index] = current;
            detected[index++] = newType;
        }

indexব্যবহারের ডিফল্ট মানটি দয়া করে লক্ষ্য করুন । আকারের উপলভ্য "অংশ" এর মাধ্যমে একটি সহজ অনুসন্ধান সেই সীমাবদ্ধতাটি সরিয়ে ফেলবে। একটি সম্পূর্ণ আকার সনাক্ত করতে আপনাকে একটি বিজ্ঞপ্তি বাফার ব্যবহার করতে হবে দয়া করে নোট করুন:

এখানে চিত্র বর্ণনা লিখুন

ক্লকওয়াইজ এবং ঘড়ির কাঁটার বিপরীতে

উভয় মোডকে সমর্থন করার জন্য আপনাকে পূর্ববর্তী বর্ধন থেকে বিজ্ঞপ্তি বাফারটি ব্যবহার করতে হবে এবং উভয় দিকেই অনুসন্ধান করতে হবে:

এখানে চিত্র বর্ণনা লিখুন

একটি উপবৃত্ত আঁকুন

boundsঅ্যারেতে আপনার প্রয়োজনীয় সমস্ত কিছু রয়েছে ।

এখানে চিত্র বর্ণনা লিখুন

কেবলমাত্র সেই ডেটা ব্যবহার করুন:

cWidth = bounds[2].y - bounds[0].y;
cHeight = bounds[3].y - bounds[1].y;

অন্যান্য অঙ্গভঙ্গি (alচ্ছিক)

শেষ অবধি, অন্য অঙ্গভঙ্গিগুলিকে সমর্থন করার জন্য আপনার dx(বা dy) শূন্যের সমান হলে সঠিকভাবে একটি পরিস্থিতি পরিচালনা করতে হবে:

এখানে চিত্র বর্ণনা লিখুন

হালনাগাদ

এই ছোট পিওসিটি বেশ উচ্চ মনোযোগ পেয়েছে, তাই কোডটি সুচারুভাবে কাজ করতে এবং কিছু অঙ্কনের ইঙ্গিত প্রদান করতে, সমর্থনকারী পয়েন্টগুলি হাইলাইট করার জন্য আমি কিছুটা আপডেট করেছি:

এখানে চিত্র বর্ণনা লিখুন

কোডটি এখানে:

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.HeadlessException;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class CircleGestureDemo extends JFrame {

    enum Type {

        RIGHT_DOWN,
        LEFT_DOWN,
        LEFT_UP,
        RIGHT_UP,
        UNDEFINED
    }

    private static final Type[] circleShape = {
        Type.RIGHT_DOWN,
        Type.LEFT_DOWN,
        Type.LEFT_UP,
        Type.RIGHT_UP};

    public CircleGestureDemo() throws HeadlessException {
        super("Circle gesture");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new BorderLayout());
        add(BorderLayout.CENTER, new GesturePanel());
        setPreferredSize(new Dimension(800, 600));
        pack();
    }

    public static class GesturePanel extends JPanel implements MouseListener, MouseMotionListener {

        private boolean editing = false;
        private Point[] bounds;
        private Point last = new Point(0, 0);
        private final List<Point> points = new ArrayList<>();

        public GesturePanel() {
            super(true);
            addMouseListener(this);
            addMouseMotionListener(this);
        }

        @Override
        public void paint(Graphics graphics) {
            super.paint(graphics);

            Dimension d = getSize();
            Graphics2D g = (Graphics2D) graphics;

            RenderingHints qualityHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            qualityHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

            g.setRenderingHints(qualityHints);

            if (!points.isEmpty() && cD == 0) {
                isCircle(points, g);
                g.setColor(HINT_COLOR);
                if (bounds[2] != null) {
                    int r = (bounds[2].y - bounds[0].y) / 2;
                    g.setStroke(new BasicStroke(r / 3 + 1));
                    g.drawOval(bounds[0].x - r, bounds[0].y, 2 * r, 2 * r);
                } else if (bounds[1] != null) {
                    int r = bounds[1].x - bounds[0].x;
                    g.setStroke(new BasicStroke(r / 3 + 1));
                    g.drawOval(bounds[0].x - r, bounds[0].y, 2 * r, 2 * r);
                }
            }

            g.setStroke(new BasicStroke(2));
            g.setColor(Color.RED);

            if (cD == 0) {
                Point b = null;
                for (Point e : points) {
                    if (null != b) {
                        g.drawLine(b.x, b.y, e.x, e.y);
                    }
                    b = e;
                }

            } else if (cD > 0) {
                g.setColor(Color.BLUE);
                g.setStroke(new BasicStroke(3));
                g.drawOval(cX, cY, cD, cD);
            } else {
                g.drawString("Uknown", 30, 50);
            }
        }

        private Type getType(int dx, int dy) {
            Type result = Type.UNDEFINED;

            if (dx > 0 && dy < 0) {
                result = Type.RIGHT_DOWN;
            } else if (dx < 0 && dy < 0) {
                result = Type.LEFT_DOWN;
            } else if (dx < 0 && dy > 0) {
                result = Type.LEFT_UP;
            } else if (dx > 0 && dy > 0) {
                result = Type.RIGHT_UP;
            }

            return result;
        }

        private boolean isCircle(List<Point> points, Graphics2D g) {
            boolean result = false;
            Type[] shape = circleShape;
            bounds = new Point[shape.length];

            final int STEP = 5;
            int index = 0;
            int initial = 0;
            Point current = points.get(0);
            Type type = null;

            for (int i = STEP; i < points.size(); i += STEP) {
                final Point next = points.get(i);
                final int dx = next.x - current.x;
                final int dy = -(next.y - current.y);

                if (dx == 0 || dy == 0) {
                    continue;
                }

                final int marker = 8;
                if (null != g) {
                    g.setColor(Color.BLACK);
                    g.setStroke(new BasicStroke(2));
                    g.drawOval(current.x - marker/2, 
                               current.y - marker/2, 
                               marker, marker);
                }

                Type newType = getType(dx, dy);
                if (type == null || type != newType) {
                    if (newType != shape[index]) {
                        break;
                    }
                    bounds[index++] = current;
                }

                type = newType;
                current = next;
                initial = i;

                if (index >= shape.length) {
                    result = true;
                    break;
                }
            }
            return result;
        }

        @Override
        public void mousePressed(MouseEvent e) {
            cD = 0;
            points.clear();
            editing = true;
        }

        private int cX;
        private int cY;
        private int cD;

        @Override
        public void mouseReleased(MouseEvent e) {
            editing = false;
            if (points.size() > 0) {
                if (isCircle(points, null)) {
                    int r = Math.abs((bounds[2].y - bounds[0].y) / 2);
                    cX = bounds[0].x - r;
                    cY = bounds[0].y;
                    cD = 2 * r;
                } else {
                    cD = -1;
                }
                repaint();
            }
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            Point newPoint = e.getPoint();
            if (editing && !last.equals(newPoint)) {
                points.add(newPoint);
                last = newPoint;
                repaint();
            }
        }

        @Override
        public void mouseMoved(MouseEvent e) {
        }

        @Override
        public void mouseEntered(MouseEvent e) {
        }

        @Override
        public void mouseExited(MouseEvent e) {
        }

        @Override
        public void mouseClicked(MouseEvent e) {
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                CircleGestureDemo t = new CircleGestureDemo();
                t.setVisible(true);
            }
        });
    }

    final static Color HINT_COLOR = new Color(0x55888888, true);
}

76
দর্শনীয় উত্তর রেনাত। পদ্ধতির স্পষ্ট বিবরণ, প্রক্রিয়া নথিবদ্ধ চিত্রগুলি, অ্যানিমেশনগুলিও। সর্বাধিক সাধারণীকরণযোগ্য, দৃust় সমাধানও মনে হয়। স্পর্শকাতরতা সত্যিই চতুর ধারণা মত শোনাচ্ছে - অনেকটা প্রাথমিক (বর্তমান?) হাতের লেখার স্বীকৃতি কৌশলগুলির মতো। এই উত্তরের জন্য প্রশ্ন বুকমার্ক করা। :)
enhzflep

27
আরও সাধারণভাবে: একটি সংক্ষিপ্ত, বোধগম্য ব্যাখ্যা এবং চিত্র এবং একটি অ্যানিমেটেড ডেমো এবং কোড এবং বিভিন্নতা? এটি একটি আদর্শ স্ট্যাক ওভারফ্লো উত্তর।
পিটার হোসি

11
এই যেমন একটি ভাল উত্তর, আমি প্রায় ক্ষমা করতে পারেন তিনি জাভা কম্পিউটার গ্রাফিক্স করছেন! ;)
নিকোলাস মিয়ারি

4
এই ক্রিসমাস, সান্তা রেনাটের জন্য কি আর অবাক করা আপডেট (যেমন, আরও আকার, ইত্যাদি) থাকবে? :-)
Unheilig

1
কি দারুন. সফর দে বল.
wogsland

14

একটি আকৃতি সনাক্ত করার জন্য একটি ক্লাসিক কম্পিউটার ভিশন কৌশল হফ ট্রান্সফর্ম। হাফ ট্রান্সফর্ম সম্পর্কে একটি দুর্দান্ত বিষয় হ'ল এটি আংশিক তথ্য, অসম্পূর্ণ ডেটা এবং গোলমাল সম্পর্কে অত্যন্ত সহনশীল। একটি বৃত্তের জন্য হাফ ব্যবহার করা: http://en.wikedia.org/wiki/Hough_transfor#Circle_detection_process

আপনার চেনাশোনাটি আঁকতে দেওয়া হয়েছে বলে আমি মনে করি হাফ রূপান্তরটি আপনার পক্ষে একটি ভাল মিল হতে পারে।

এখানে একটি "সরলিকৃত" ব্যাখ্যা দেওয়া হয়েছে, এটি সত্যিকারের এত সহজ না হওয়ার জন্য আমি ক্ষমাপ্রার্থী। এর বেশিরভাগই একটি স্কুল প্রকল্প থেকে যা আমি বহু বছর আগে করেছি।

হাফ ট্রান্সফর্ম একটি ভোটদান প্রকল্প। একটি দুটি দ্বিমাত্রিক অ্যারে পূর্ণসংখ্যা বরাদ্দ করা হয় এবং সমস্ত উপাদান শূন্য সেট করা হয়। প্রতিটি উপাদান বিশ্লেষণ করা ইমেজের একক পিক্সেলের সাথে মিলে যায়। এই অ্যারেটিকে সংশ্লেষক অ্যারে হিসাবে উল্লেখ করা হয় যেহেতু প্রতিটি উপাদান তথ্য, ভোট সংগ্রহ করবে, পিক্সেলটি একটি বৃত্ত বা চাপের উত্স হতে পারে এমন সম্ভাবনা নির্দেশ করে।

গ্রেডিয়েন্ট অপারেটর এজ ডিটেক্টরটি ইমেজটিতে প্রয়োগ করা হয় এবং প্রান্ত পিক্সেল, বা প্রান্তগুলি রেকর্ড করা হয়। একটি প্রান্ত একটি পিক্সেল যা এর প্রতিবেশীদের সাথে আলাদা আলাদা তীব্রতা বা রঙ ধারণ করে। পার্থক্যের ডিগ্রিটিকে গ্রেডিয়েন্ট মাপদণ্ড বলে। পর্যাপ্ত পরিমাণের প্রতিটি প্রান্তের জন্য একটি ভোটিং স্কিম প্রয়োগ করা হয়েছে যা সঞ্চয়ের অ্যারের উপাদানগুলিকে বাড়িয়ে তুলবে। যে উপাদানগুলিকে বর্ধিত করা হচ্ছে (ভোট দেওয়া হয়েছে) এমন চেনাশোনাগুলির সম্ভাব্য উত্সের সাথে মিল রয়েছে যা বিবেচনাধীন প্রান্তের মধ্য দিয়ে যায়। কাঙ্ক্ষিত ফলাফলটি হ'ল যদি কোনও চাপ থাকে তবে সত্য উত্সটি মিথ্যা উত্সের চেয়ে বেশি ভোট পাবে।

নোট করুন যে ভোটদানের জন্য পরিদর্শনকৃত অ্যারের উপাদানগুলি বিবেচনাধীন প্রান্তের চারপাশে একটি বৃত্ত তৈরি করে। ভোটদানের জন্য x, y স্থানাঙ্কগুলি গণনা করা আপনি যে বৃত্তটি আঁকছেন তার x, y স্থানাঙ্ক গণনা করার সমান।

আপনার হাতে টানা চিত্রটিতে আপনি প্রান্ত গণনা না করে সরাসরি সেট (রঙিন) পিক্সেল ব্যবহার করতে পারবেন।

এখন অসম্পূর্ণভাবে অবস্থিত পিক্সেল সহ আপনি অগত্যা সর্বাধিক সংখ্যক ভোটের সাথে একটি একক সঞ্চায়ক অ্যারে উপাদান পাবেন না। আপনি একগুচ্ছ ভোট, একটি গুচ্ছ সহ প্রতিবেশী অ্যারে উপাদানগুলির সংগ্রহ পেতে পারেন। এই গুচ্ছের মাধ্যাকর্ষণ কেন্দ্রটি উত্সটির জন্য একটি ভাল আনুমানিক প্রস্তাব দিতে পারে।

দ্রষ্টব্য যে আপনাকে ব্যাসার্ধ আর এর বিভিন্ন মানের জন্য হাফ ট্রান্সফর্ম চালাতে হতে পারে votes যে ভোটের ঘন ক্লাস্টার উত্পাদন করে তা হ'ল "আরও ভাল" ফিট।

ভুয়া উত্সের জন্য ভোট কমাতে ব্যবহার করার বিভিন্ন কৌশল রয়েছে। উদাহরণস্বরূপ প্রান্তগুলি ব্যবহার করার একটি সুবিধা হ'ল এগুলির কেবল মাত্রার পরিমাণ নেই তবে তাদের দিকনির্দেশও রয়েছে। ভোট দেওয়ার সময় আমাদের কেবলমাত্র সঠিক দিকের সম্ভাব্য উত্সগুলির পক্ষে ভোট প্রয়োজন। ভোট প্রাপ্ত স্থানগুলি সম্পূর্ণ বৃত্তের পরিবর্তে একটি চাপ তৈরি করবে।

এখানে একটি উদাহরণ। আমরা এক ব্যাসার্ধের একটি বৃত্ত এবং একটি প্রাথমিক আরম্ভকৃত অ্যারে দিয়ে শুরু করি। প্রতিটি পিক্সেল হিসাবে বিবেচিত সম্ভাব্য উত্স জন্য ভোট দেওয়া হয়। প্রকৃত উত্স সবচেয়ে বেশি ভোট পায় যা এক্ষেত্রে চারটি।

.  empty pixel
X  drawn pixel
*  drawn pixel currently being considered

. . . . .   0 0 0 0 0
. . X . .   0 0 0 0 0
. X . X .   0 0 0 0 0
. . X . .   0 0 0 0 0
. . . . .   0 0 0 0 0

. . . . .   0 0 0 0 0
. . X . .   0 1 0 0 0
. * . X .   1 0 1 0 0
. . X . .   0 1 0 0 0
. . . . .   0 0 0 0 0

. . . . .   0 0 0 0 0
. . X . .   0 1 0 0 0
. X . X .   1 0 2 0 0
. . * . .   0 2 0 1 0
. . . . .   0 0 1 0 0

. . . . .   0 0 0 0 0
. . X . .   0 1 0 1 0
. X . * .   1 0 3 0 1
. . X . .   0 2 0 2 0
. . . . .   0 0 1 0 0

. . . . .   0 0 1 0 0
. . * . .   0 2 0 2 0
. X . X .   1 0 4 0 1
. . X . .   0 2 0 2 0
. . . . .   0 0 1 0 0

5

এখানে অন্য উপায়। ইউআইভিউ টাচবেগান, স্পর্শমোভড, স্পর্শএন্ড ব্যবহার করে এবং অ্যারেতে পয়েন্ট যুক্ত করা হচ্ছে। আপনি অ্যারেটিকে অর্ধে ভাগ করেছেন এবং পরীক্ষা করুন যে একটি অ্যারের প্রতিটি বিন্দু অন্য জোড়ের মতো অন্য অ্যারেতে এর সমকক্ষ থেকে প্রায় একই ব্যাস কিনা।

    NSMutableArray * pointStack;

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        // Detect touch anywhere
    UITouch *touch = [touches anyObject];


    pointStack = [[NSMutableArray alloc]init];

    CGPoint touchDownPoint = [touch locationInView:touch.view];


    [pointStack addObject:touchDownPoint];

    }


    /**
     * 
     */
    - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
    {

            UITouch* touch = [touches anyObject];
            CGPoint touchDownPoint = [touch locationInView:touch.view];

            [pointStack addObject:touchDownPoint];  

    }

    /**
     * So now you have an array of lots of points
     * All you have to do is find what should be the diameter
     * Then compare opposite points to see if the reach a similar diameter
     */
    - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
    {
            uint pointCount = [pointStack count];

    //assume the circle was drawn a constant rate and the half way point will serve to calculate or diameter
    CGPoint startPoint = [pointStack objectAtIndex:0];
    CGPoint halfWayPoint = [pointStack objectAtIndex:floor(pointCount/2)];

    float dx = startPoint.x - halfWayPoint.x;
    float dy = startPoint.y - halfWayPoint.y;


    float diameter = sqrt((dx*dx) + (dy*dy));

    bool isCircle = YES;// try to prove false!

    uint indexStep=10; // jump every 10 points, reduce to be more granular

    // okay now compare matches
    // e.g. compare indexes against their opposites and see if they have the same diameter
    //
      for (uint i=indexStep;i<floor(pointCount/2);i+=indexStep)
      {

      CGPoint testPointA = [pointStack objectAtIndex:i];
      CGPoint testPointB = [pointStack objectAtIndex:floor(pointCount/2)+i];

      dx = testPointA.x - testPointB.x;
      dy = testPointA.y - testPointB.y;


      float testDiameter = sqrt((dx*dx) + (dy*dy));

      if(testDiameter>=(diameter-10) && testDiameter<=(diameter+10)) // +/- 10 ( or whatever degree of variance you want )
      {
      //all good
      }
      else
      {
      isCircle=NO;
      }

    }//end for loop

    NSLog(@"iCircle=%i",isCircle);

}

ঠিক আছে? :)


3

আমি কোনও আকৃতি স্বীকৃতি বিশেষজ্ঞ নই, তবে আমি কীভাবে সমস্যাটির কাছে যেতে পারি তা এখানে।

প্রথমে ব্যবহারকারীর পাথকে ফ্রিহ্যান্ড হিসাবে প্রদর্শন করার সময়, গোপনে বারের সাথে বিন্দু (x, y) নমুনার একটি তালিকা সংগ্রহ করুন। আপনি আপনার টেনে আনার ইভেন্টগুলি থেকে উভয় তথ্য পেতে পারেন, এগুলিকে একটি সাধারণ মডেল অবজেক্টে গুটিয়ে রাখতে পারেন এবং এগুলি একটি পরিবর্তনীয় অ্যারেতে পাইল করতে পারেন।

আপনি সম্ভবত নমুনাগুলি মোটামুটি ঘন ঘন নিতে চান — বলুন, প্রতি 0.1 সেকেন্ডে। আরেকটি সম্ভাবনা হ'ল সত্যিকারের ঘন ঘন শুরু হওয়া , সম্ভবত প্রতি 0.05 সেকেন্ডে, এবং ব্যবহারকারী কতক্ষণ টানেন তা দেখে; যদি তারা কিছু সময়ের চেয়ে বেশি সময় ধরে টানতে থাকে, তবে নমুনাটির ফ্রিকোয়েন্সিটি (এবং যে কোনও নমুনা যে মিস হয়েছে তা ফেলে দিন) ০.২ সেকেন্ডের মতো কিছুতে।

(এবং সুসমাচারের জন্য আমার নম্বরগুলি গ্রহণ করবেন না, কারণ আমি তাদের কেবল আমার টুপি থেকে টেনে এনেছি Exper পরীক্ষা করে আরও ভাল মান খুঁজে পাই))

দ্বিতীয়ত, নমুনাগুলি বিশ্লেষণ করুন।

আপনি দুটি তথ্য উপস্থাপন করতে চাইবেন। প্রথমে, আকৃতির কেন্দ্র, যা (আইআইআরসি) কেবলমাত্র সমস্ত পয়েন্টের গড় হওয়া উচিত। দ্বিতীয়ত, কেন্দ্র থেকে প্রতিটি নমুনার গড় ব্যাসার্ধ।

@ ব্যবহারকারী 1118321 অনুমান হিসাবে, আপনি বহুভুজকে সমর্থন করতে চান, তবে বিশ্লেষণের বাকী অংশটি সেই সিদ্ধান্তটি নিয়ে গঠিত: ব্যবহারকারী কোনও বৃত্ত বা বহুভুজ আঁকতে চান কিনা। সেই সংকল্পটি করার জন্য আপনি নমুনাটিকে বহুভুজ হিসাবে দেখতে পারেন can

আপনি ব্যবহার করতে পারেন বিভিন্ন মানদণ্ড রয়েছে:

  • সময়: ব্যবহারকারী যদি অন্যের চেয়ে কিছু পয়েন্টে বেশি সময় ধরে ঘুরে বেড়ায় (যা যদি নমুনাগুলি স্থির বিরতিতে থাকে তবে মহাকাশে একে অপরের কাছাকাছি একটানা নমুনার ক্লাস্টার হিসাবে উপস্থিত হয়), সেগুলি কোণ হতে পারে। আপনার কোণার প্রান্তটি ছোট করা উচিত যাতে ব্যবহারকারী প্রতিটি কোণায় ইচ্ছাকৃতভাবে বিরতি দেওয়ার পরিবর্তে অজ্ঞান হয়ে এটি করতে পারে।
  • কোণ: একটি চেনাশোনাতে প্রায় সমস্ত নমুনা থেকে পরের নমুনা পর্যন্ত প্রায় একই কোণ থাকবে। একটি বহুভুজের সরলরেখার অংশে একাধিক কোণ যুক্ত হবে; কোণগুলি কোণে। একটি নিয়মিত বহুভুজ (একটি অনিয়মিত বহুভুজ এর উপবৃত্তাকার বৃত্ত) জন্য, কোণ কোণগুলি মোটামুটি একই হতে হবে; একটি অনিয়মিত বহুভুজের বিভিন্ন কোণার কোণ থাকবে।
  • বিরতি: একটি নিয়মিত বহুভুজের কোণগুলি কৌণিক মাত্রার মধ্যে পৃথক পৃথক স্থান হবে এবং ব্যাসার্ধ স্থির থাকবে। একটি অনিয়মিত বহুভুজের অনিয়মিত কৌণিক অন্তর এবং / অথবা একটি অ-ধ্রুবক ব্যাসার্ধ থাকবে।

তৃতীয় এবং চূড়ান্ত পদক্ষেপটি পূর্ব নির্ধারিত ব্যাসার্ধের সাথে পূর্ব নির্ধারিত কেন্দ্র বিন্দুটিকে কেন্দ্র করে আকারটি তৈরি করা।

আমি উপরে যা কিছু বলেছি তা কার্যকর হবে বা দক্ষ হবে তার কোনও গ্যারান্টি নেই, তবে আমি আশা করি এটি কমপক্ষে আপনাকে সঠিক পথে পৌঁছে দিয়েছে please এবং দয়া করে, যদি আমার চেয়ে আকৃতি স্বীকৃতি সম্পর্কে আরও বেশি কিছু জানেন (তবে এটি একটি খুব কম বার) দেখেন তবে এটি, কোনও মন্তব্য বা আপনার নিজের উত্তর পোস্ট করতে নির্দ্বিধায়।


+1 হাই, ইনপুটটির জন্য ধন্যবাদ। খুবই তথ্যবহুল. তেমনি, আমিও চাই যে আইওএস / "আকৃতি স্বীকৃতি" সুপারম্যান কোনওভাবে এই পোস্টটি দেখবে এবং আমাদের আরও আলোকিত করবে।
আনহেলিগ

1
@ আনহেলিগ: ভাল ধারণা সম্পন্ন.
পিটার হোসি 18

1
আপনার অ্যালগরিদম ভাল লাগছে। আমি নিখুঁত চেনাশোনা / বহুভুজ থেকে ব্যবহারকারীর পথ কত দূরে সরিয়ে নিয়েছে তার একটি চেক যোগ করব। (উদাহরণস্বরূপ, শতাংশ মানে বর্গ বিচ্যুতি)) এটি যদি খুব বড় হয় তবে ব্যবহারকারী আদর্শ আকৃতিটি না চাইতে পারে। দক্ষ ডুডলারের ক্ষেত্রে কাটাফটি ঝাল ডুডলারের চেয়ে ছোট হবে। এটি থাকার ফলে প্রোগ্রামটি শিল্পীদের শৈল্পিক স্বাধীনতা প্রদান করতে পারে তবে প্রাথমিকভাবে প্রচুর সহায়তা করে।
dmm

@ ব্যবহারকারী2654818: আপনি কীভাবে এটি পরিমাপ করবেন?
পিটার হোসে 19

1
@ পিটারহসে: চেনাশোনাগুলির জন্য ব্যাখ্যা: একবার আপনার আদর্শ বৃত্তটি পরে, আপনি কেন্দ্র এবং ব্যাসার্ধ পেয়ে গেছেন। সুতরাং আপনি প্রতিটি টানা বিন্দু নিয়ে যান এবং কেন্দ্র থেকে বর্গক্ষেত্রের দূরত্ব গণনা করুন যা ((x-x0) ^ 2 + (y-y0) ^ 2)। ব্যাসার্ধের স্কোয়ার থেকে বিয়োগ করুন। (গণনা বাঁচাতে আমি প্রচুর স্কোয়ার শিকড় এড়িয়ে চলেছি)) টানা পয়েন্টের জন্য স্কোয়ার ত্রুটিটিকে কল করুন। সমস্ত টানা পয়েন্টের জন্য স্কোয়ার ত্রুটি গড়, তারপরে এটি বর্গক্ষেত্র, তারপরে এটি ব্যাসার্ধ দ্বারা ভাগ করুন। এটি আপনার গড় শতাংশ বিচ্যুতি। (গণিত / পরিসংখ্যান সম্ভবত ক্রিঞ্জ-যোগ্য, তবে এটি অনুশীলনে কার্যকর হবে))
মিম

2

আমি সঠিকভাবে প্রশিক্ষিত $ 1 সনাক্তকারী ( http://depts.washington.edu/aimgroup/proj/dollar/) এর সাথে আমার বেশ সৌভাগ্য হয়েছে had ) । আমি এটিকে চেনাশোনা, লাইন, ত্রিভুজ এবং স্কোয়ারের জন্য ব্যবহার করেছি।

এটি ইউআইজিস্টারআরগনাইজাইজারের আগে অনেক আগে ছিল, তবে আমি মনে করি উপযুক্ত ইউআইজিস্টচারআরকনাইজার সাবক্লাসগুলি তৈরি করা সহজ-ইশ হওয়া উচিত।


2

একবার আপনি যখন ব্যবহারকারীটি শুরু করেছিলেন সেখান থেকে তাদের আকৃতি আঁকানো শেষ হয়ে গেলে, আপনি যে স্থানাঙ্কগুলি করেছিলেন সেগুলির একটি নমুনা নিতে পারেন এবং সেগুলি একটি বৃত্তে লাগানোর চেষ্টা করতে পারেন।

এই সমস্যার জন্য এখানে একটি ম্যাটল্যাব সমাধান রয়েছে: http://www.mathworks.com.au/matlabcentral/fileexchange/15060-fitcircle-m

যা ওয়াল্টার গ্যান্ডার, জিন এইচ। গোলুব এবং রল্ফ স্ট্রবেলের লেস্ট -স্কোয়ার্স ফিটিং অফ সার্কেল অ্যান্ড এলিপেসিসের কাগজের উপর ভিত্তি করে তৈরি হয়েছে : http://www.emis.de/journals/BBMS/Bletletin/sup962/gender.pdf

ক্যানটারবেরি বিশ্ববিদ্যালয় থেকে ডাঃ ইয়ান কোপ, এনজেড বিমূর্তের সাথে একটি প্রবন্ধ প্রকাশ করেছেন:

প্লেনের পয়েন্টগুলির একটি সেট (বা স্পষ্ট সাধারণীকরণের সাথে n- মাত্রাগুলি) সেরা ফিটের বৃত্ত নির্ধারণ করার সমস্যাটি সহজেই একটি ননলাইনারের সর্বনিম্ন সর্বনিম্ন-স্কোয়ার সমস্যা হিসাবে তৈরি করা হয় যা গাউস-নিউটন মিনিমাইজেশন অ্যালগরিদম ব্যবহার করে সমাধান করা যেতে পারে। এই সোজা-এগিয়ে দেওয়া পদ্ধতির বহিরাগতদের উপস্থিতির পক্ষে অদক্ষ এবং অত্যন্ত সংবেদনশীল হিসাবে দেখানো হয়। একটি বিকল্প গঠনের ফলে সমস্যাটিকে একটি লিনিয়ার সর্বনিম্ন স্কোয়ার সমস্যার মধ্যে হ্রাস করা যায় যা তুচ্ছভাবে সমাধান করা হয়। প্রস্তাবিত পদ্ধতির অদলৈখিক ন্যূনতম স্কোয়ার পদ্ধতির চেয়ে বহিরাগতদের কাছে খুব কম সংবেদনশীল হওয়ার অতিরিক্ত সুবিধা রয়েছে বলে দেখানো হয়েছে।

http://link.springer.com/article/10.1007%2FBF00939613

ম্যাটল্যাব ফাইলটি ননলাইনার টিএলএস এবং লিনিয়ার এলএলএস সমস্যা উভয়ই গণনা করতে পারে।


0

এখানে ব্যবহার করার জন্য মোটামুটি সহজ উপায়:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

এই ম্যাট্রিক্স গ্রিডটি ধরে নেওয়া:

 A B C D E F G H
1      X X
2    X     X 
3  X         X
4  X         X
5    X     X
6      X X
7
8

"এক্স" অবস্থানগুলিতে কিছু ইউআইভিউ রাখুন এবং হিট হওয়ার জন্য তাদের পরীক্ষা করুন (ক্রম অনুসারে)। যদি তারা সবাই ক্রমানুসারে হিট হয় তবে আমি মনে করি এটি ব্যবহারকারীর পক্ষে "ভাল কাজ আপনি একটি বৃত্ত আঁকেন" বলতে দেওয়া উচিত হবে

ভাল লাগছে? (এবং সাধারণ)


হাই, লেবু ভাল যুক্তি, কিন্তু উপরের দৃশ্যে, এর অর্থ হ'ল আমাদের স্পর্শগুলি সনাক্ত করতে 64৪ টি ইউআইভিউ থাকা দরকার, তাই না? উদাহরণস্বরূপ, যদি ক্যানভাস কোনও আইপ্যাডের আকার হয় তবে আপনি কীভাবে একটি একক ইউআইভিউর জন্য আকারটি সংজ্ঞায়িত করবেন? মনে হচ্ছে বৃত্তটি যদি ছোট হয় এবং যদি একটি একক ইউআইভিউয়ের আকার বড় হয় তবে এই ক্ষেত্রে আমরা ক্রমটি পরীক্ষা করতে পারিনি কারণ সমস্ত টানা পয়েন্টগুলি একটি একক ইউআইভিউয়ের মধ্যেই থাকবে।
আনহেলিগ

হ্যাঁ - এটি সম্ভবত কেবল তখনই কাজ করে যদি আপনি ক্যানভাসটি 300x300 এর মতো কিছুতে সংশোধন করেন এবং তারপরে তার পাশে একটি "উদাহরণ" ক্যানভাস থাকে যার সাথে আপনি যে বৃত্তটি আঁকতে চান তার আকারের সাথে। যদি তাই হয় তবে আমি 50x50 স্কোয়ার * 6 দিয়ে যেতে চাই, আপনি কেবলমাত্র সঠিক অবস্থানগুলিতে হিট করতে আগ্রহী এমন দৃশ্যগুলি রেন্ডার করতে হবে, সমস্ত 6 * 6 (36) বা 8 * 8 (64) নয়
সেজি

@ আনহেলিগ: এই সমাধানটিই তাই করে। দর্শনের সঠিক অনুক্রমের মধ্য দিয়ে যাওয়ার জন্য পর্যাপ্ত পরিমাণে কোনও কিছুই (এবং আপনি অতিরিক্ত opালু পথের জন্য কয়েকটি সর্বাধিক সংখ্যক পথকে সম্ভাব্যভাবে অনুমতি দিতে পারেন) একটি বৃত্ত হিসাবে মিলবে। তারপরে আপনি এগুলি সমস্ত দর্শনের কেন্দ্রে কেন্দ্র করে একটি নিখুঁত বৃত্তে নিয়ে যান, যার ব্যাসার্ধ সমস্ত (বা কমপক্ষে বেশিরভাগ) এর কাছে পৌঁছায়।
পিটার হোসি 18

@ পিটারহসে ঠিক আছে, আমাকে এদিকে মাথা ফেরাতে চেষ্টা করুন। আপনারা কেউ যদি এই রোলিংটি পেতে কিছু কোড সরবরাহ করতে পারে তবে আমি প্রশংসা করব। এদিকে, আমি এটির জন্য আমার মাথাটি আরও ঘুরিয়ে নেওয়ার চেষ্টা করব এবং পরে কোডিং অংশটি দিয়ে আমি একই কাজ করব। ধন্যবাদ।
আনহেলিগ

আপনার জন্য
সবেমাত্র
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.