저번에 Tesseract와 OpenALPR을 이용하여 C# 프로그램을 만들었는데 이번에는 안드로이드에서 만들어보겠습니다!!


참고 ☞ C# 문자인식 프로그래밍


안드로이드에서 문자인식을 하기위해서는 기본적으로 OpenCV 라이브러리가 필요합니다.


참고 ☞ 안드로이드에서 OpenCV 사용하기


안드로이드에서는 API를 사용하지 않고 조금 원시적인(?) 방법으로 문자인식으로 해보려고 합니다.


※ 문자인식을 하기 위한 절차


1. 안드로이드와 카메라를 연동합니다.(CameraPreview)

2. 사진을 촬영하고 내부저장소에 저장합니다.(PNG)

3. 저장소에 저장된 이미지파일을 불러옵니다.(Bitmap)

4. 불러온 이미지 파일에 전처리 작업을 수행합니다.(GrayScale, ThresHold, 등)

5. 전체 이미지에서 번호판 영역을 관심영역으로 설정하고 따로 분류합니다.(ROI, contours)

6. 분류된 번호판 영역 이미지 파일에서 번호판 숫자 및 문자를 라벨링 처리합니다.

7. 라벨링 처리 된 숫자 및 문자들을 미리 준비한 템플릿과 비교합니다.

8. 비교한 결과 중 가장 매칭률이 높은 숫자 및 문자를 선별합니다.

9. 모든 숫자와 문자를 순서대로 조합하면 문자인식 결과입니다.


안드로이드에서도 물론 Tesseract API(tess-two)를 사용할 수 있습니다. (30줄 이내에...) 

하지만 이런 원시적인 방법을 통해 실제로 문자 및 패턴인식을 하는 원리를 파악할 수 있으며 가장 중요한것은 인식률이 상당히 높다는 것입니다!!! (Tesseract OCR이 한글 문자 인식률이 매우 낮은걸로 유명하죠... 하지만 워낙 라이브러리 자체가 유연해서 거의 모든 프로그래밍 언어를 지원하며 문자 인식 훈련을 통해 인식률을 높일수 있답니다.)


자 그럼 절차대로 하나하나씩 따라해봅시다.


1, 2. 안드로이드 카메라 연동 및 사진 저장 : 이미 블로그에 설명한 내용이 있으므로 링크로 대체합니다.


3. 저장된 이미지 파일 불러오기 : 내부 저장소에 저장된 이미지 파일을 불러오는 방법은 다음과 같이 간단합니다. PNG파일을 File 객체를 통해 지정하고 BitmapFactory.decodeFile을 통해 이미지를 비트맵 객체로 불러올 수 있습니다.


File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/OpenCVTest/sample.png");

if(file.exists()){

Bitmap myBitmap = BitmapFactory.decodeFile(file.getAbsolutePath());


4. 이미지 전처리 작업 : 기본적으로 GrayScale(흑백) 및 ThresHold(이진화) 처리를 수행합니다.


Bitmap image1;

OpenCVLoader.initDebug();


Mat img1=new Mat();

Utils.bitmapToMat(myBitmap ,img1);


Mat imageGray1 = new Mat();

Mat imageCny1 = new Mat();


Imgproc.cvtColor(img1, imageGray1, Imgproc.COLOR_BGR2GRAY);

Imgproc.threshold(imageGray1, imageCny1, 160, 255, Imgproc.THRESH_BINARY);


5. 이미지 관심영역 추출 : 링크


6, 7, 8, 9. 번호판 숫자 인식 : 

코드가 약간 복잡합니다. 간단히 설명드리면 다음 절차와 같이 수행합니다.

1. 전체사진 불러오기 -> 전처리 -> 번호판만 추출

2. 번호판사진 불러오기 -> 흑백 반전(검은바탕에 흰색 숫자가 되도록) -> 숫자 추출

3. 추출된 숫자와 템플릿 비교

4. 결과 출력


* 신형 번호판의 숫자와 문자는 00가 0000 이런식으로 숫자6개 문자1개 총 7개의 라벨이 생깁니다. 하지만 'ㄱ ㅏ' 라는 문자의 경우 'ㄱ'과 'ㅏ'를 따로 라벨링 처리하기 때문에 총 8개의 문자가 생길때도 있습니다. 이를 처리하기위해 라벨이 7개, 8개인 경우를 각각 조건문으로 처리했습니다.


int[][] listarr = new int[8][4];

List<MatOfPoint> contoursROI = new ArrayList<>();

Mat hierarchyROI = new Mat();

Mat matROI = new Mat();

Mat ResultROI = new Mat();

Utils.bitmapToMat(roi, matROI);

Imgproc.cvtColor(matROI, ResultROI, Imgproc.COLOR_BGR2GRAY, 1);

Imgproc.findContours(ResultROI, contoursROI, hierarchyROI, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

int count = 0;

for(int idx = 0; idx >= 0; idx = (int) hierarchyROI.get(0, idx)[0]) {

    if(count>7) break;

    MatOfPoint matOfPoint = contoursROI.get(idx);

    Rect rect = Imgproc.boundingRect(matOfPoint);

    // 해상도별로 조절하기.

    if(rect.x < roi.getWidth()/20 || rect.x > roi.getWidth()-(roi.getWidth()/10) ||

            rect.y < roi.getHeight()/10 || rect.y > roi.getHeight()-(roi.getHeight()/10) ||

            rect.width < roi.getWidth()/50 || rect.width > roi.getWidth()/8 ||

            rect.height <= roi.getHeight()/10 ) continue;


    Log.d("RECT : ", "x : " + rect.x + ", y : " + rect.y + ", w :" + rect.width + ", h : " + rect.height);

    Imgproc.rectangle(matROI, rect.tl(), rect.br(), new Scalar(255, 0, 0, 255), 1);


    listarr[count][0] = rect.x;

    listarr[count][1] = rect.y;

    listarr[count][2] = rect.width;

    listarr[count][3] = rect.height;

    count++;

}

if(count == 7) {

    // 오름차순 정렬

    for(int i = 0; i < 7; i++) {

        for(int j = i; j < 7; j++) {

            if(listarr[i][0] > listarr[j][0]) {

                // 스왑

                int[] temp = new int[4];

                temp[0] = listarr[i][0];

                temp[1] = listarr[i][1];

                temp[2] = listarr[i][2];

                temp[3] = listarr[i][3];

                listarr[i][0] = listarr[j][0];

                listarr[i][1] = listarr[j][1];

                listarr[i][2] = listarr[j][2];

                listarr[i][3] = listarr[j][3];

                listarr[j][0] = temp[0];

                listarr[j][1] = temp[1];

                listarr[j][2] = temp[2];

                listarr[j][3] = temp[3];

            }

        }

    }


    for(int i = 0; i < 7; i++) {

        Bitmap tempBitmap = Bitmap.createBitmap(roi, listarr[i][0], listarr[i][1], listarr[i][2], listarr[i][3]);

        tempBitmap = Bitmap.createScaledBitmap(tempBitmap, 10, 15, true);

        if(i == 0)

            imageViewROI1.setImageBitmap(tempBitmap);

        else if(i == 1)

            imageViewROI2.setImageBitmap(tempBitmap);

        else if(i == 2)

            imageViewROI3.setImageBitmap(tempBitmap);

        else if(i == 3)

            imageViewROI4.setImageBitmap(tempBitmap);

        else if(i == 4)

            imageViewROI5.setImageBitmap(tempBitmap);

        else if(i == 5)

            imageViewROI6.setImageBitmap(tempBitmap);

        else if(i == 6)

            imageViewROI7.setImageBitmap(tempBitmap);


        int num_count = 0;

        double max = 0.0;

        int num = 0;

        if(i==2) { // 문자

            for(int n = 0; n < 10; n++) {

                for(int j = 0; j < 15; j++) {

                    for(int k = 0; k < 10; k++) {

                        if(ones[n][j][k] == tempBitmap.getPixel(k, j))

                            num_count++;

                    }

                }

                double result = (double)num_count/proones[n]*100;

                if(result > 100) result = 100;

                if(result > max) {

                    max = result;

                    num = n;

                }

                num_count=0;

            }

        }

        else { // 숫자

            for(int n = 0; n < 10; n++) {

                for(int j = 0; j < 15; j++) {

                    for(int k = 0; k < 10; k++) {

                        if(nums[n][j][k] == tempBitmap.getPixel(k, j))

                            num_count++;

                    }

                }

                double result = (double)num_count/pronums[n]*100;

                if(result > 100) result = 100;

                if(result > max) {

                    max = result;

                    num = n;

                }

                num_count=0;

            }

        }


        avg+=max;

        String floatnum = String.format("%.2f", max);


        if(i == 2) { // 한글 처리

            // 중략


            }


            int a = 0, b = 0, c = 0;

            for(int x=0; x<tempBitmap.getHeight(); x++) {

                for (int y = 0; y < tempBitmap.getWidth(); y++) {

                    if (tempBitmap.getPixel(y, x) == -1) {

                        Log.d("arr", "ones[5][" + a + "][" + b + "] = " + tempBitmap.getPixel(y, x) + ";");

                        c++;

                    }

                    b++;

                }

                b = 0;

                a++;

            }

            Log.d("count" , ""+c);

        }

        else { // 숫자 처리

            textViewResult.append(i + "번째 분석 결과 : " + num + " / 정확도 : " + floatnum +"%\n");

            result += Integer.toString(num);

        }

    }

}

else if(count == 8) {

    // 오름차순 정렬

    for(int i = 0; i < 8; i++) {

        for(int j = i; j < 8; j++) {

            if(listarr[i][0] > listarr[j][0]) {

                // 스왑

                int[] temp = new int[4];

                temp[0] = listarr[i][0];

                temp[1] = listarr[i][1];

                temp[2] = listarr[i][2];

                temp[3] = listarr[i][3];

                listarr[i][0] = listarr[j][0];

                listarr[i][1] = listarr[j][1];

                listarr[i][2] = listarr[j][2];

                listarr[i][3] = listarr[j][3];

                listarr[j][0] = temp[0];

                listarr[j][1] = temp[1];

                listarr[j][2] = temp[2];

                listarr[j][3] = temp[3];

            }

        }

    }

    // 한글인 경우

    if(abs(listarr[2][0]-listarr[3][0]) < 20) {

        if(listarr[2][1] > listarr[3][1]) {

            // 스왑

            int[] temp = new int[4];

            temp[0] = listarr[2][0];

            temp[1] = listarr[2][1];

            temp[2] = listarr[2][2];

            temp[3] = listarr[2][3];

            listarr[2][0] = listarr[3][0];

            listarr[2][1] = listarr[3][1];

            listarr[2][2] = listarr[3][2];

            listarr[2][3] = listarr[3][3];

            listarr[3][0] = temp[0];

            listarr[3][1] = temp[1];

            listarr[3][2] = temp[2];

            listarr[3][3] = temp[3];

        }

    }


    for(int i = 0; i < 8; i++) {

        Bitmap tempBitmap = Bitmap.createBitmap(roi, listarr[i][0], listarr[i][1], listarr[i][2], listarr[i][3]);

        tempBitmap = Bitmap.createScaledBitmap(tempBitmap, 10, 15, true);

        if(i == 0)

            imageViewROI1.setImageBitmap(tempBitmap);

        else if(i == 1)

            imageViewROI2.setImageBitmap(tempBitmap);

        else if(i == 2)

            imageViewROI3.setImageBitmap(tempBitmap);

        else if(i == 3)

            imageViewROI4.setImageBitmap(tempBitmap);

        else if(i == 4)

            imageViewROI5.setImageBitmap(tempBitmap);

        else if(i == 5)

            imageViewROI6.setImageBitmap(tempBitmap);

        else if(i == 6)

            imageViewROI7.setImageBitmap(tempBitmap);

        else if(i == 7)

            imageViewROI8.setImageBitmap(tempBitmap);


        int num_count = 0;

        double max = 0.0;

        int num = 0;

        // 자음

        if(i==2) {

            for(int n = 0; n < 10; n++) {

                for(int j = 0; j < 15; j++) {

                    for(int k = 0; k < 10; k++) {

                        if(cons[n][j][k] == tempBitmap.getPixel(k, j))

                            num_count++;

                    }

                }

                double result = (double)num_count/procons[n]*100;

                if(result > 100) result = 100;

                if(result > max) {

                    max = result;

                    num = n;

                }

                num_count=0;

            }

        }


        // 모음

        else if(i==3) {

            for(int n = 0; n < 4; n++) {

                for(int j = 0; j < 15; j++) {

                    for(int k = 0; k < 10; k++) {

                        if(vocs[n][j][k] == tempBitmap.getPixel(k, j))

                            num_count++;

                    }

                }

                double result = (double)num_count/provocs[n]*100;

                if(result > 100) result = 100;

                if(result > max) {

                    max = result;

                    num = n;

                }

                num_count=0;

            }

        }


        // 숫자

        else {

            for(int n = 0; n < 10; n++) {

                for(int j = 0; j < 15; j++) {

                    for(int k = 0; k < 10; k++) {

                        if(nums[n][j][k] == tempBitmap.getPixel(k, j))

                            num_count++;

                    }

                }

                double result = (double)num_count/pronums[n]*100;

                if(result > 100) result = 100;

                if(result > max) {

                    max = result;

                    num = n;

                }

                num_count=0;

            }

        }


        avg+=max;

        String floatnum = String.format("%.2f", max);


        // 자음

        if(i==2) {

            // 중략

        }


        // 모음

        else if(i==3) {

            // 중략

        }


        // 숫자

        else {

            textViewResult.append(i + "번째 분석 결과 : " + num + " / 정확도 : " + floatnum +"%\n");

            result += Integer.toString(num);

        }

    }

}

else {

    Toast.makeText(context, "숫자 생성 실패", Toast.LENGTH_SHORT).show();

}


Utils.matToBitmap(matROI, roi2);


imageViewROI = (ImageView)findViewById(R.id.image_result_ROI);

imageViewROI.setImageBitmap(roi2);


String resultnum = String.format("%.2f", avg/count);

textViewResult.append("\n\n분석 결과 : " + result + " / 정확도 : " + resultnum + "%");

}


중간에 문자 비교 코드는 너무 길어서 중략 처리했습니다.

다음은 미리 저장해둔 문자 템플릿입니다.


nums = new int[10][15][10];

pronums = new int[10];


cons = new int[10][15][10];

procons = new int[10];


vocs = new int[4][15][10];

provocs = new int[4];


ones = new int[10][15][10];

proones = new int[10];


// 픽셀 개수

pronums[0] = 50;

pronums[1] = 40;

pronums[2] = 40;

pronums[3] = 45;

pronums[4] = 50;

pronums[5] = 55;

pronums[6] = 50;

pronums[7] = 37;

pronums[8] = 50;

pronums[9] = 40;


// 0

nums[0][0][3] = -1; nums[0][0][4] = -1; nums[0][0][5] = -1; nums[0][0][6] = -1;

nums[0][1][2] = -1; nums[0][1][3] = -1; nums[0][1][6] = -1; nums[0][1][7] = -1;

nums[0][2][0] = -1; nums[0][2][1] = -1; nums[0][2][8] = -1; nums[0][2][9] = -1;

nums[0][3][0] = -1; nums[0][3][1] = -1; nums[0][3][8] = -1; nums[0][3][9] = -1;

nums[0][4][0] = -1; nums[0][4][1] = -1; nums[0][4][8] = -1; nums[0][4][9] = -1;

nums[0][5][0] = -1; nums[0][5][1] = -1; nums[0][5][8] = -1; nums[0][5][9] = -1;

nums[0][6][0] = -1; nums[0][6][1] = -1; nums[0][6][8] = -1; nums[0][6][9] = -1;

nums[0][7][0] = -1; nums[0][7][1] = -1; nums[0][7][8] = -1; nums[0][7][9] = -1;

nums[0][8][0] = -1; nums[0][8][1] = -1; nums[0][8][8] = -1; nums[0][8][9] = -1;

nums[0][9][0] = -1; nums[0][9][1] = -1; nums[0][9][8] = -1; nums[0][9][9] = -1;

nums[0][10][0] = -1; nums[0][10][1] = -1; nums[0][10][8] = -1; nums[0][10][9] = -1;

nums[0][11][0] = -1; nums[0][11][1] = -1; nums[0][11][8] = -1; nums[0][11][9] = -1;

nums[0][12][0] = -1; nums[0][12][1] = -1; nums[0][12][8] = -1; nums[0][12][9] = -1;

nums[0][13][2] = -1; nums[0][13][3] = -1; nums[0][13][6] = -1; nums[0][13][7] = -1;

nums[0][14][3] = -1; nums[0][14][4] = -1; nums[0][14][5] = -1; nums[0][14][6] = -1;


// 1

nums[1][0][7] = -1; nums[1][0][8] = -1;

nums[1][1][6] = -1; nums[1][1][7] = -1; nums[1][1][8] = -1;

nums[1][2][1] = -1; nums[1][2][2] = -1; nums[1][2][3] = -1; nums[1][2][4] = -1; nums[1][2][5] = -1; nums[1][2][6] = -1; nums[1][2][7] = -1; nums[1][2][8] = -1;

nums[1][3][0] = -1; nums[1][3][1] = -1; nums[1][3][2] = -1; nums[1][3][3] = -1; nums[1][3][4] = -1; nums[1][3][5] = -1; nums[1][3][6] = -1; nums[1][3][7] = -1; nums[1][3][8] = -1;

nums[1][4][6] = -1; nums[1][4][7] = -1; nums[1][4][8] = -1;

nums[1][5][7] = -1; nums[1][5][8] = -1;

nums[1][6][7] = -1; nums[1][6][8] = -1;

nums[1][7][7] = -1; nums[1][7][8] = -1;

nums[1][8][7] = -1; nums[1][8][8] = -1;

nums[1][9][7] = -1; nums[1][9][8] = -1;

nums[1][10][7] = -1; nums[1][10][8] = -1;

nums[1][11][7] = -1; nums[1][11][8] = -1;

nums[1][12][7] = -1; nums[1][12][8] = -1;

nums[1][13][7] = -1; nums[1][13][8] = -1;

nums[1][14][7] = -1; nums[1][14][8] = -1;


// 2

nums[2][0][3] = -1; nums[2][0][4] = -1;

nums[2][1][1] = -1; nums[2][1][2] = -1; nums[2][1][5] = -1; nums[2][1][6] = -1;

nums[2][2][0] = -1; nums[2][2][1] = -1; nums[2][2][7] = -1;

nums[2][3][0] = -1; nums[2][3][1] = -1; nums[2][3][7] = -1; nums[2][3][8] = -1;

nums[2][4][7] = -1; nums[2][4][8] = -1;

nums[2][5][7] = -1; nums[2][5][8] = -1;

nums[2][6][6] = -1; nums[2][6][7] = -1;

nums[2][7][6] = -1; nums[2][7][7] = -1;

nums[2][8][5] = -1; nums[2][8][6] = -1;

nums[2][9][4] = -1; nums[2][9][5] = -1;

nums[2][10][3] = -1; nums[2][10][4] = -1;

nums[2][11][3] = -1; nums[2][11][4] = -1;

nums[2][12][2] = -1; nums[2][12][3] = -1;

nums[2][13][1] = -1; nums[2][13][2] = -1; nums[2][13][3] = -1;

nums[2][14][1] = -1; nums[2][14][2] = -1; nums[2][14][3] = -1; nums[2][14][4] = -1; nums[2][14][5] = -1; nums[2][14][6] = -1; nums[2][14][7] = -1; nums[2][14][8] = -1; nums[2][14][9] = -1;


// 3

nums[3][0][2] = -1; nums[3][0][3] = -1; nums[3][0][4] = -1; nums[3][0][5] = -1; nums[3][0][6] = -1; nums[3][0][7] = -1; nums[3][0][8] = -1; nums[3][0][9] = -1;

nums[3][1][3] = -1; nums[3][1][4] = -1; nums[3][1][5] = -1; nums[3][1][6] = -1; nums[3][1][7] = -1; nums[3][1][8] = -1; nums[3][1][9] = -1;

nums[3][2][7] = -1; nums[3][2][8] = -1;

nums[3][3][6] = -1; nums[3][3][7] = -1;

nums[3][4][5] = -1; nums[3][4][6] = -1;

nums[3][5][4] = -1; nums[3][5][5] = -1; nums[3][5][6] = -1;

nums[3][6][5] = -1; nums[3][6][6] = -1; nums[3][6][7] = -1;

nums[3][7][7] = -1;

nums[3][8][7] = -1; nums[3][8][8] = -1;

nums[3][9][7] = -1; nums[3][9][8] = -1;

nums[3][10][0] = -1; nums[3][10][1] = -1; nums[3][10][7] = -1;

nums[3][11][0] = -1; nums[3][11][1] = -1; nums[3][11][7] = -1;

nums[3][12][0] = -1; nums[3][12][1] = -1; nums[3][12][6] = -1;

nums[3][13][1] = -1; nums[3][13][2] = -1; nums[3][13][3] = -1; nums[3][13][4] = -1; nums[3][13][5] = -1; nums[3][13][6] = -1;

nums[3][14][2] = -1; nums[3][14][3] = -1; nums[3][14][4] = -1;


// 4

nums[4][0][6] = -1; nums[4][0][7] = -1;

nums[4][1][6] = -1; nums[4][1][7] = -1;

nums[4][2][5] = -1; nums[4][2][6] = -1; nums[4][2][7] = -1;

nums[4][3][4] = -1; nums[4][3][5] = -1; nums[4][3][6] = -1; nums[4][3][7] = -1;

nums[4][4][3] = -1; nums[4][4][4] = -1; nums[4][4][6] = -1; nums[4][4][7] = -1;

nums[4][5][3] = -1; nums[4][5][6] = -1; nums[4][5][7] = -1;

nums[4][6][2] = -1; nums[4][6][3] = -1; nums[4][6][6] = -1; nums[4][6][7] = -1;

nums[4][7][2] = -1; nums[4][7][6] = -1; nums[4][7][7] = -1;

nums[4][8][1] = -1; nums[4][8][2] = -1; nums[4][8][6] = -1; nums[4][8][7] = -1;

nums[4][9][1] = -1; nums[4][9][6] = -1; nums[4][9][7] = -1;

nums[4][10][0] = -1; nums[4][10][1] = -1; nums[4][10][2] = -1; nums[4][10][3] = -1; nums[4][10][4] = -1; nums[4][10][5] = -1; nums[4][10][6] = -1; nums[4][10][7] = -1; nums[4][10][8] = -1; nums[4][10][9] = -1;

nums[4][11][0] = -1; nums[4][11][1] = -1; nums[4][11][2] = -1; nums[4][11][3] = -1; nums[4][11][4] = -1; nums[4][11][5] = -1; nums[4][11][6] = -1; nums[4][11][7] = -1; nums[4][11][8] = -1; nums[4][11][9] = -1;

nums[4][12][6] = -1; nums[4][12][7] = -1;

nums[4][13][6] = -1; nums[4][13][7] = -1;

nums[4][14][6] = -1; nums[4][14][7] = -1;


// 5

nums[5][0][2] = -1; nums[5][0][3] = -1; nums[5][0][4] = -1; nums[5][0][5] = -1; nums[5][0][6] = -1; nums[5][0][7] = -1; nums[5][0][8] = -1;

nums[5][1][1] = -1;  nums[5][1][2] = -1; nums[5][1][3] = -1; nums[5][1][4] = -1; nums[5][1][5] = -1; nums[5][1][6] = -1; nums[5][1][7] = -1;

nums[5][2][1] = -1; nums[5][2][2] = -1;

nums[5][3][1] = -1; nums[5][3][2] = -1;

nums[5][4][1] = -1; nums[5][4][2] = -1;

nums[5][5][1] = -1; nums[5][5][2] = -1;

nums[5][6][1] = -1; nums[5][6][2] = -1; nums[5][6][3] = -1; nums[5][6][4] = -1; nums[5][6][5] = -1; nums[5][6][6] = -1; nums[5][6][7] = -1;

nums[5][7][1] = -1; nums[5][7][2] = -1; nums[5][7][3] = -1; nums[5][7][4] = -1; nums[5][7][5] = -1; nums[5][7][6] = -1; nums[5][7][7] = -1; nums[5][7][8] = -1;

nums[5][8][8] = -1; nums[5][8][9] = -1;

nums[5][9][8] = -1; nums[5][9][9] = -1;

nums[5][10][8] = -1; nums[5][10][9] = -1;

nums[5][11][0] = -1; nums[5][11][1] = -1; nums[5][11][8] = -1; nums[5][11][9] = -1;

nums[5][12][0] = -1; nums[5][12][1] = -1; nums[5][12][7] = -1; nums[5][12][8] = -1;

nums[5][13][1] = -1; nums[5][13][2] = -1; nums[5][13][3] = -1; nums[5][13][4] = -1; nums[5][13][5] = -1; nums[5][13][6] = -1; nums[5][13][7] = -1;

nums[5][14][2] = -1; nums[5][14][3] = -1; nums[5][14][4] = -1; nums[5][14][5] = -1; nums[5][14][6] = -1;


// 6

nums[6][0][6] = -1; nums[6][0][7] = -1;

nums[6][1][6] = -1;

nums[6][2][5] = -1; nums[6][2][6] = -1;

nums[6][3][4] = -1; nums[6][3][5] = -1;

nums[6][4][4] = -1;

nums[6][5][3] = -1; nums[6][5][4] = -1;

nums[6][6][2] = -1; nums[6][6][3] = -1; nums[6][6][4] = -1; nums[6][6][5] = -1; nums[6][6][6] = -1;

nums[6][7][2] = -1; nums[6][7][3] = -1; nums[6][7][4] = -1; nums[6][7][5] = -1; nums[6][7][6] = -1; nums[6][7][7] = -1; nums[6][7][8] = -1;

nums[6][8][1] = -1; nums[6][8][2] = -1; nums[6][8][7] = -1; nums[6][8][8] = -1;

nums[6][9][0] = -1; nums[6][9][1] = -1; nums[6][9][8] = -1; nums[6][9][9] = -1;

nums[6][10][0] = -1; nums[6][10][1] = -1; nums[6][10][8] = -1; nums[6][10][9] = -1;

nums[6][11][0] = -1; nums[6][11][1] = -1; nums[6][11][8] = -1; nums[6][11][9] = -1;

nums[6][12][0] = -1; nums[6][12][1] = -1; nums[6][12][2] = -1; nums[6][12][7] = -1; nums[6][12][8] = -1;

nums[6][13][1] = -1; nums[6][13][2] = -1; nums[6][13][3] = -1; nums[6][13][4] = -1; nums[6][13][5] = -1; nums[6][13][6] = -1; nums[6][13][7] = -1;

nums[6][14][3] = -1; nums[6][14][4] = -1; nums[6][14][5] = -1;


// 7

nums[7][0][0] = -1; nums[7][0][1] = -1; nums[7][0][2] = -1; nums[7][0][3] = -1; nums[7][0][4] = -1; nums[7][0][5] = -1; nums[7][0][6] = -1; nums[7][0][7] = -1; nums[7][0][8] = -1; nums[7][0][9] = -1;

nums[7][1][0] = -1; nums[7][1][1] = -1; nums[7][1][2] = -1; nums[7][1][6] = -1; nums[7][1][7] = -1; nums[7][1][8] = -1; nums[7][1][9] = -1;

nums[7][2][0] = -1; nums[7][2][1] = -1; nums[7][2][7] = -1; nums[7][2][8] = -1;

nums[7][3][7] = -1;

nums[7][4][7] = -1;

nums[7][5][6] = -1;

nums[7][6][5] = -1; nums[7][6][6] = -1;

nums[7][7][5] = -1;

nums[7][8][5] = -1;

nums[7][9][4] = -1; nums[7][9][5] = -1;

nums[7][10][4] = -1;

nums[7][11][3] = -1; nums[7][11][4] = -1;

nums[7][12][3] = -1;

nums[7][13][2] = -1; nums[7][13][3] = -1;

nums[7][14][2] = -1;


// 8

nums[8][0][4] = -1; nums[8][0][5] = -1;

nums[8][1][2] = -1; nums[8][1][3] = -1; nums[8][1][4] = -1; nums[8][1][5] = -1; nums[8][1][6] = -1; nums[8][1][7] = -1;

nums[8][2][1] = -1; nums[8][2][2] = -1; nums[8][2][7] = -1; nums[8][2][8] = -1;

nums[8][3][1] = -1; nums[8][3][8] = -1;

nums[8][4][0] = -1; nums[8][4][1] = -1; nums[8][4][8] = -1;

nums[8][5][1] = -1; nums[8][5][8] = -1;

nums[8][6][2] = -1; nums[8][6][3] = -1; nums[8][6][4] = -1; nums[8][6][5] = -1; nums[8][6][6] = -1; nums[8][6][7] = -1;

nums[8][7][2] = -1; nums[8][7][3] = -1; nums[8][7][4] = -1; nums[8][7][5] = -1; nums[8][7][6] = -1; nums[8][7][7] = -1;

nums[8][8][1] = -1; nums[8][8][2] = -1; nums[8][8][7] = -1; nums[8][8][8] = -1;

nums[8][9][0] = -1; nums[8][9][1] = -1; nums[8][9][8] = -1; nums[8][9][9] = -1;

nums[8][10][0] = -1; nums[8][10][1] = -1; nums[8][10][8] = -1; nums[8][10][9] = -1;

nums[8][11][0] = -1; nums[8][11][1] = -1; nums[8][11][8] = -1; nums[8][11][9] = -1;

nums[8][12][1] = -1; nums[8][12][2] = -1; nums[8][12][7] = -1; nums[8][12][8] = -1;

nums[8][13][1] = -1; nums[8][13][2] = -1; nums[8][13][3] = -1; nums[8][13][6] = -1; nums[8][13][7] = -1;

nums[8][14][3] = -1; nums[8][14][4] = -1; nums[8][14][5] = -1; nums[8][14][6] = -1;


// 9

nums[9][0][3] = -1; nums[9][0][4] = -1; nums[9][0][5] = -1;

nums[9][1][1] = -1; nums[9][1][2] = -1; nums[9][1][3] = -1; nums[9][1][4] = -1; nums[9][1][5] = -1; nums[9][1][6] = -1; nums[9][1][7] = -1;

nums[9][2][0] = -1; nums[9][2][1] = -1; nums[9][2][7] = -1; nums[9][2][8] = -1;

nums[9][3][0] = -1; nums[9][3][1] = -1; nums[9][3][7] = -1; nums[9][3][8] = -1;

nums[9][4][0] = -1; nums[9][4][1] = -1; nums[9][4][7] = -1; nums[9][4][8] = -1;

nums[9][5][1] = -1; nums[9][5][2] = -1; nums[9][5][8] = -1; nums[9][5][9] = -1;

nums[9][6][1] = -1; nums[9][6][2] = -1; nums[9][6][3] = -1; nums[9][6][8] = -1; nums[9][6][9] = -1;

nums[9][7][3] = -1; nums[9][7][4] = -1; nums[9][7][5] = -1; nums[9][7][6] = -1; nums[9][7][7] = -1; nums[9][7][8] = -1;

nums[9][8][5] = -1; nums[9][8][6] = -1; nums[9][8][7] = -1;

nums[9][9][6] = -1; nums[9][9][7] = -1;

nums[9][10][6] = -1;

nums[9][11][6] = -1; nums[9][11][7] = -1;

nums[9][12][5] = -1;

nums[9][13][4] = -1; nums[9][13][5] = -1;


// 자음 및 모음 중략...


이런식으로 3차원 배열에 이미지 템플릿(자음, 모음, 숫자)에 해당하는 픽셀값을 미리 설정해 두고 추출된 이미지와 1:1 비교해서 일치하는 값만큼 숫자를 증가시켜 그 숫자의 비율을 정확도로 칭하고 그 정확도가 가장 높은 숫자를 인식된 숫자라고 판별합니다.


다음은 실행 결과입니다.





프로그램 코드가 너무 긴 관계로 일부 코드를 생략했습니다. 무조건 복사 붙여넣기 한다고 실행 되지 않을것입니다. 프로그램 원리나 소스코드에 대해 궁금하신분은 댓글 또는 방명록에 남겨주시면 상세하게 답변해드리겠습니다. 

공감♡ 버튼을 눌러주시면 더욱 유용하고 좋은 포스팅으로 찾아뵙겠습니다.









ESP8266 + OV2640

ESP8266 과 OV2640을 이용하여 영상처리와 패턴인식 프로그래밍을 해보려고 합니다.
위 사진은 ArduCam ESP8266 과 OV2640 결합 사진입니다.
배선은 따로 필요없으며 위 사진과 같이 결합 해주시면 됩니다.
하지만 코드를 업로드 하기위해서 Arduino IDE에 새로운 보드를 추가해야합니다.

* 아두이노 보드 추가 방법
1. 아두이노 스케쳐를 실행하고 파일 - 환경설정에 들어갑니다.
2. 추가적인 보드매니저 URLs 맨 오른쪽 버튼을 누르고 다음과 같이 입력합니다.
3. http://www.arducam.com/downloads/ESP8266_UNO/package_ArduCAM_index.json
4. 입력 후 확인버튼을 누릅니다.
5. 툴 - 보드 - 보드매니저로 들어갑니다.
6. ESP8266을 검색하고 보드를 설치합니다.

* 예제코드
1. 파일 - 예제 - ArduCAM - ESP8266 - ArduCAM_ESP8266_OV2640_Capture를 실행합니다.
2. memorysaver.h 를 찾아 메모장 또는 VS로 연 다음 #define OV2640_CAM 주석을 제외합니다.
3. 다시 아두이노 스케쳐로 돌아와서 다음 부분을 수정합니다.
const char* ssid = "SSID"; // SSID 대신 와이파이 이름을 적습니다.
const char* password = "PASSWORD"; // PASSWORD 대신 와이파이 비밀번호를 적습니다.
4. 업로드 버튼을 누르고 시리얼 모니터를 실행합니다.
5. 글자가 깨져보이면 보드레이트를 115200으로 맞춰줍니다.
다음과 같이 출력이 나온다면 성공입니다.
실패시 오류 화면을 캡쳐 또는 복사해서 댓글로 달아주세요.
- Connecting to "WIFI이름" - 192.168. ~~ : url 주소입니다.

* 출력화면
1. 인터넷 창을 켜서 주소 입력란에 위의 IP주소를 입력하고 /capture를 입력하여 접속해봅시다.
예) 192.168.0.0/capture
2. IP주소/stream 입력 시 화면에 연속된 영상이 출력됩니다.
다음과 같이 캡쳐된 화면이 인터넷창에 출력됩니다. 만약 초점이 안맞을 시 OV2640 카메라 앞부분을 돌려서 초점을 맞춰봅시다.

다음은 C# 프로그램으로 아두이노로 캡쳐한 사진을 이용하여 패턴인식을 해보겠습니다.
질문사항은 댓글로 남겨주세요!



'Arduino' 카테고리의 다른 글

아두이노 블루투스(HC06) 연동  (0) 2018.09.04
[Arduino] 아두이노 DIY 스마트 책상 만들기!  (7) 2018.08.21

to Top