앞서 설정은 다 끝났으니

이제 전처리된 이미지를 Ocr 하는것이 더 잘 되는지 실험해본다. 

 

테스트 클래스 생성!! 

import org.opencv.core.Core; 
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;

import org.opencv.core.Size;
import org.opencv.highgui.HighGui;
import org.opencv.imgproc.Imgproc;

public class OpenCVTest {
	 private static final int 
     CV_THRESH_OTSU = 8,
     CV_THRESH_BINARY = 0,
     CV_ADAPTIVE_THRESH_MEAN_C = 0;
	 
	static {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
    }
	
	 public static void main(String[] args) {
		 try {
			String path= "경로상생략/img/";
			String target = path + "tttt.jpg";
		    	
	    	// 변환할 이미지를 저장할 객체 
			Mat gray = new Mat();
			Mat resized = new Mat();
			Mat blured = new Mat();
			Mat equalized = new Mat();
			Mat Threshold = new Mat();
			
			// image read 
			Mat img = Imgcodecs.imread(target,Imgcodecs.IMREAD_UNCHANGED);
			
			// GrayScale
			Imgproc.cvtColor(img, gray, Imgproc.COLOR_RGB2GRAY);
	
			// 밝기 조절  
			//gray.convertTo(gray, -1, 1, 150);
	
			// blur 
			Imgproc.GaussianBlur(gray, blured, new Size(0, 0), 1);
				
			// 이진화
			Imgproc.threshold(blured, Threshold, 0, 255,  CV_THRESH_OTSU + CV_THRESH_BINARY);
				
			Imgproc.adaptiveThreshold(Threshold, Threshold, 255, CV_ADAPTIVE_THRESH_MEAN_C ,CV_THRESH_BINARY, 99, 4);
	
			// Resize
			Size sizeLenna = new Size(img.width()*2, img.height()*2);
			Imgproc.resize(Threshold, resized, sizeLenna);
				
	
			// Equalized 
			Imgproc.equalizeHist(resized, equalized);
			
            // 변환된 이미지를 저장한다.
			Imgcodecs.imwrite(path+"/res.jpg", resized);
			
			// 변환된 이미지를 보여준다.
			HighGui.imshow("res", resized);
			HighGui.waitKey();
			
			// tesseract 적용
			File image = new File(path+"res.jpg");
			Tesseract tesseract = new Tesseract();
	    		tesseract.setDatapath("경로상 생략/tessdata");
			    tesseract.setLanguage("eng");
                      tesseract.setOcrEngineMode(1);
					
			String result = tesseract.doOCR(image);
			System.out.println(result);
			
			
		} catch (TesseractException e) {
			e.printStackTrace();
		}
		
			

			 
    }

}

 

설명) 

openCV에서 제공되는 여러가지 이미지 편집 기능이 있다. 

검색해보니 대부분 OCR 이전 전처리로 진행하는 순서들이 있었는데 

먼저 graySacle를 한 뒤 blur 처리 > 이진화 > 평탄화 작업을 하는거 같아 

그대로 진행해보았고, 내가 필요한건 스캔된 문서이미지를 읽어보는건데 

스캔했을때 특유의 스프레이같은 것들이 있어서 밝기 조절로 좀 지워보려고 추가해 보았다.

 

작업물을 보자. 

전)

결과 

 

 

OpenCV로 전처리한 후 

 

결과)

 

 

 

느낀점 ) 

검색해봤던 여러가지 방식을 테스트 해보았다. 

알려진 방법대로 이미지를 전처리한 뒤 확인해보니

이게 더 잘 보인다고? 하는 생각이 드는 경우가 있었다..

생각해보니 사람이 보는 이미지와 컴퓨터가 읽기 좋은 이미지는 다를테니  

어떻게 해주면 더 잘 읽어줄지 좀 더 고민을 해봐야할거 같다. 

 

결론) 

손글씨를 읽기는 역부족이다.  꼭 필요하다면 상용화된 서비스를 유료로 사용해야겠다..

( 구글에서 제공하는 유료 서비스는 잘 읽었음.. ) 

yolo 프레임웍이라는게 있다는데,, 직접 OCR을 만들어보고 싶다. 

'OCR' 카테고리의 다른 글

Java + OpenCV 사용법  (0) 2022.04.08
[Maven] Tesseract 사용법  (0) 2022.04.08
OCR 알아보기 시작!!  (0) 2022.04.07

1. 먼저 Tesseract 라이브러리를 불러온다

maven 사이트 접속해서 오늘날짜 기준 가장 최신버전으로 선택했다. 

https://mvnrepository.com/artifact/net.sourceforge.tess4j/tess4j/5.2.0

 

2. pom.xml에 dependency 추가. 

<dependency>
	<groupId>net.sourceforge.tess4j</groupId>
	<artifactId>tess4j</artifactId>
	<version>4.5.2</version>
</dependency>

 

3. 테스트를 위한 Class 생성 ! 

import java.io.File;
import net.sourceforge.tess4j.Tesseract;
import net.sourceforge.tess4j.TesseractException;


public class OcrTest {
		
	 public static void main(String[] args) {
		 try {
		    // 읽어볼 이미지를 가져온다. 
		    File image = new File("경로상생략/res.jpg");

                    Tesseract tesseract = new Tesseract();
                    tesseract.setDatapath("경로상생략/tessdata"); //** 학습된데이터가 있는 폴더를 지정해준다. 
                    tesseract.setLanguage("eng"); // 언어설정 
                    tesseract.setPageSegMode(1); // 페이지 모드 설정
                    tesseract.setOcrEngineMode(1); 
                    // tesseract.setHocr(true); // html로 그려주는 flag
                    String result = tesseract.doOCR(image);
                    System.out.println(result);

		 
		 } catch (TesseractException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		 }
		 
		 
	 }

}

  

 tesseract.setDatapath("경로상생략/tessdata");  이 라인은 tesseract가 이미지를 읽었을때 

비교할 학습된 데이터 파일의 경로를 가르킨다. 

아래 링크에 접속해보면 이미 학습된 데이터를 제공하고 있으므로 내가 하고자 하는 언어를 다운받아서 넣어두면 된다. 

https://github.com/tesseract-ocr/tessdata

 

GitHub - tesseract-ocr/tessdata: Trained models with support for legacy and LSTM OCR engine

Trained models with support for legacy and LSTM OCR engine - GitHub - tesseract-ocr/tessdata: Trained models with support for legacy and LSTM OCR engine

github.com

 

접속해보면 아래와 같은 파일들이 나오는데 앞 글자가 언어를 가르키고 있다. 

 

나는 영문과 중문이 필요하므로 아래 이미지와 같이 구성하였다. 

 

 

테스트로 사용한 이미지 

 

결과 ) 

 

 

결론)

위에 올린 이미지 말고도 여러개의 이미지를 테스트 해보았으나 

연습용 정도의 퀄리티만 나올 뿐, 업무에 적용해보긴 어려울거 같다는 판단이 든다. 

 

어떻게 하면 인식률을 올릴 수 있을까? 

 OpenCV 라이브러리를 이용해 이미지 흑백으로 바꾸고 블러등등.. 을 이용해

이미지를 더 깔끔하게 만들어 준다면  인식하는데 효과가 있다고한다.

OpenCV 적용을 해보도록한다. 

 

* 손글씨는 50% 정도만 인식되는걸로 보임. 

 

'OCR' 카테고리의 다른 글

[JAVA] OpenCV + Tesseract 사용법  (0) 2022.04.08
Java + OpenCV 사용법  (0) 2022.04.08
OCR 알아보기 시작!!  (0) 2022.04.07

자바에 오라클 DB연동을 해봅시다! 

 

1. ojdbc를 다운받아야합니다 (https://www.oracle.com/kr/database/technologies/appdev/jdbc-downloads.html

저는 java8 사용중으로 ojdbc8.jar을 다운받았다. ( 자바8이면 아래 jar 다운받으면 됨)

ojdbc8.jar
4.81MB

 

2.  라이브러리 추가해주기 

프로젝트에 JRE system ... 우클릭 후 Properties를 선택하고 아래 이미지와 같이 클릭클릭! 따라간다. 

 

 

다운받은 경로에있는 jar를 선택해주면 됩니다. 

 

적용이 완료되면 아래와 같이 라이브러리가 적용된게 보인다. 

 

자 이제 DB Connection 을 담당할 Class를 생성해주자.

DB username 과 pw를 입력하고 url은 본인이 접속하고자 하는 정보로 연결하면 된다. 나는 로컬DB로 연결~~ 

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DBHelper {
	
    public static Connection dbConn;
    
    public static Connection getConnection() {
        Connection connect = null;
        
        try {
        	// 접속정보 설정 
            String user = "아이디"; 
            String pw = "비번";
            String url = "jdbc:oracle:thin:@localhost:1521:orcl";
            
            // 드라이버 셋팅 
            Class.forName("oracle.jdbc.driver.OracleDriver");
            
            // 연결! 
            connect = DriverManager.getConnection(url, user, pw);
            
            System.out.println("----------OK;");
           
        } catch (ClassNotFoundException cnfe) {
            System.out.println("DB 드라이버 로딩 실패 :"+cnfe.toString());
        } catch (SQLException sqle) {
            System.out.println("DB 접속실패 : "+sqle.toString());
        } catch (Exception e) {
            System.out.println("Unkonwn error");
            e.printStackTrace();
        }
        return connect;     
    }

    public static void main(String[] args) {
    	// 연결테스트 
    	getConnection();
	}
}

 

자 이제 실행해보면  연결이 잘되었다면 콘솔창에 "DB Connection OK;" 문구가 출력된다.~ 

 

 

 

여기까지 잘 되었다면 이제 CRUD를 실행해보자 ~~

 


import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;



public class MyTest {

    public static void main(String[] args) {

		Connection con = null; // 데이터베이스에 접속하기 위한 변수
		Statement stmt = null; // SQL 구문을 실행하기 위한 변수
		ResultSet rs = null; // Select 구문 실행했을 때 결과를 저장하기 위한 변수
		PreparedStatement pstmt = null; // 삽입을 위한 Statement 변수 선언

		try {

			/*******************************************
			 * DB CONNECTION
			 ******************************************/
			con = DBHelper.getConnection();
			System.out.println(con);
			
			

			/*******************************************
			 * INSERT
			 ******************************************/
			pstmt = con.prepareStatement("insert into 테이블명 (컬럼명1, 컬럼명2) values(?, ? )");
			// 물음표에 들어갈 값들을 정의해준다. 
			pstmt.setString(1, "A1");
			pstmt.setString(2, "A2");
			int result = pstmt.executeUpdate();
			pstmt.close();
			
			
			
			/*******************************************
			 * SELECT
			 ******************************************/
			stmt = con.createStatement();
			rs = stmt.executeQuery("select * from 테이블명");
			while (rs.next()) {
				// 컬럼 순서대로 타입에 맞춰 출력하면 된다.
				// 첫번째 컬럼이 숫자 타입인 경우 rs.getInt(1)
				// 두번째 컬럼이 문자 타입인 경우 rs.getString(2)
				System.out.println(rs.getInt(1) + ":" + rs.getString(2));
			}
			;
			stmt.close();
			if (rs.next()) System.out.println("데이터가 없습니다.");
			
			
			

			/*******************************************
			 * UPDATE
			 ******************************************/
			String tmp = "UPDATE 테이블명 SET 컬럼명 = 업데이트할 값  WHERE 조건";
			pstmt = con.prepareStatement(tmp);
			pstmt.executeUpdate();
			pstmt.close();

			
			
			
			/*******************************************
			 * DELETE
			 ******************************************/
			stmt = con.createStatement();
			rs = stmt.executeQuery("delete from 테이블명 where 조건");
			stmt.close();
			
			

		} catch (SQLException e) {
			e.printStackTrace();

		} finally {
			try {
				if (con != null) con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}

	}

}

 

만약 실행 중 에러가 발생하거나 DB에 업데이트가 안되는 경우 DB툴에서 COMMIT;  한번해주기~~ 

 

 

끝! 

 

 

SpringBoot Session Timeout 설정을 해보겠습니다~ 

 

1. bean을 정의해둔 config 파일에 SessionListener 등록! 

@Bean
public HttpSessionListener httpSessionListener(){
	return new SessionListener();
}

 

2. SessionListener 파일 생성! 

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;


public class SessionListener  implements HttpSessionListener{

	@Override
	public void sessionCreated(HttpSessionEvent session) {
		// 초단위로 설정된다. 테스트로 300(5분)으로 설정
        session.getSession().setMaxInactiveInterval(60*5);
	}

	@Override
	public void sessionDestroyed(HttpSessionEvent arg0) {
	}

}

 

3. 시간이 잘 설정되었는지 확인하기! Controller에 세션을 확장할 메소드를 하나 생성한다. 

 

@PostMapping("/extendSession")
ResponseBody
public boolean  extendSession() {
	logger.info("로그인 세션 연장");
	logger.info(request.getSession().getMaxInactiveInterval()); 
	logger.info("==============================");
      
    // 리턴값으론 로그인 여부를 리턴해주고있다. 본인코드에 맞춰 수정하기 
	return 로그인여부(true or false) ; 
}

 

이 메소드를 호출하면 아래 이미지처럼 설정된 시간이 초단위로 출력된다. 

* 설정한 값으로 변경이 안된다면 브라우저 캐시 지우고 다시 실행하자!! 

 

 

Session 만료를 사용자에게 알리기 위해 script 작업을 추가했다. 

main_layout.html 에 스크립트 추가~ ( 전역에서 사용되는 공간에 추가하면 됩니다. )

 

//<![CDATA[
        $(function () {
        	
            // 세션을 5분으로 설정해둬서 5로 설정함! 자기 시간에 맞춰 설정하기
            // 숫자 5는 상수값으로 처리해서 사용하면 좋겠다! 
         	var sessionTimeChecker = 5;
            
            // 1분에 한번씩 인터발을 이용해 남은 시간을 계산한다. 
            var sessionInterval = setInterval(checkSession, 60000);
            
            // 비동기 요청을 캐치해 시간을 리셋해준다. 
            $(document).ajaxStart(function () {
            	if(loginck) { // 로그인 여부 확인 후 세션 리셋 
	            	sessionTimeChecker = 5;
            	} else { 
                	// 로그인이 안된 상태라면 인터발 종료 
            		clearInterval(sessionInterval);	
            	}
             });
            
            
            
            // 1분에 한번씩 호출되는 함수로 1분이 남았을때 alert를 출력해준다. 
            function checkSession() { 
		sessionTimeChecker -= 1; // 1분씩 마이너스해서 남은 시간을 계산한다. 
		console.log("인터발 동작 " + sessionTimeChecker +"  " +  new Date() );
            	
                // 1분이 남은 경우 알림창 띄운다. 
                // 확인버튼을 눌렀으나 이미 로그아웃된 경우는 만료알림 문구를 출력하고 페이지를 새로고침한다.
            	if(sessionTimeChecker == 1) { 
            		if(confirm("장시간 사용되지 않아 1분뒤 자동으로 로그아웃됩니다. \n로그인 시간을 연장하시려면 확인 버튼을 클릭해주세요.")) { 
            			$.post("/extendSession").done(function(data) {
            				if(!data) {
            					alert("유효시간 만료로 로그아웃 되었습니다.");
            					location.reload(); // 리로드되면 로그인화면으로 자동 변경됨 
            				}
            			});
            		}
            	}	
            }
            
            
        });
        
        //]]>

 

* 여러번 반복되는 숫자들은 상수로 처리해서 하나만 수정하면 전부 수정되도록 코딩하는게 좋다.

아래 코드는 샘플로 값을 바로 확인하기 위해 그대로 입력해둠!  

 

1. 로그인 페이지 만들기! 

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
	<title>Home</title>
	<script src="https://developers.kakao.com/sdk/js/kakao.min.js"></script>
</head>
<body>

  
    <h1>로그인</h1>
    <hr>

    <form action="/user/login" method="post">
        <input type="text" name="username" placeholder="이메일 입력해주세요">
        <input type="password" name="password" placeholder="비밀번호">
        <button type="submit">로그인</button>
        
        
  		<a href="http://developers.kakao.com/logout"></a>
  		
    </form>
    
	<a href="https://kauth.kakao.com/oauth/authorize?client_id={클라이언트키}&redirect_uri={리다이렉트 url}&response_type=code">로그인</a>
	<a href="https://kauth.kakao.com/oauth/authorize?response_type=code&client_id={클라이언트아이디}&scope=profile,account_email&state=Z3JNGXqbBZCtif_8D0hz5hWhuAeftMPNUIeCOW4_Kj4%3D&redirect_uri={리다이렉트 url}">동의</a>
    
    
    <script src="/resources/css/bower_components/jquery/dist/jquery.min.js"></script>
	<script src="/resources/css/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
	<script src="/resources/css/dist/js/adminlte.min.js"></script>
	<script src="/resources/js/utils/easy_ui/jquery.easyui.min.js"></script>
   

   
</body>
</html>


 

 

controller 

import java.util.HashMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.me.genie.service.KakaoService;

@Controller
public class KakaoController {
	
	@Autowired private KakaoService kakaoService;
	
	@RequestMapping("/login")
    public String home(@RequestParam(value = "code", required = false) String code, Model model) throws Exception{
        System.out.println("#########" + code);
        
        /******************************************************
         *  카카오 리턴 값들~
         *****************************************************/
        String access_Token = "";
        if(code != null ) { 
        	access_Token = kakaoService.getAccessToken(code);
        }
        if(access_Token != "") { 
        	System.out.println("###access_Token#### : " + access_Token);
        	HashMap<String, Object> userInfo = kakaoService.getUserInfo(access_Token);
        	System.out.println("###access_Token#### : " + access_Token);
        	System.out.println("###userInfo#### : " + userInfo.get("email"));
        	System.out.println("###nickname#### : " + userInfo.get("nickname"));
        	System.out.println("###profile_image#### : " + userInfo.get("profile_image"));
        }
       
        return "home";
    }
}

 

service 

package com.me.genie.service;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;

import org.springframework.stereotype.Service;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

@Service
public class KakaoService {


        public String getAccessToken (String authorize_code) {
            String access_Token = "";
            String refresh_Token = "";
            String reqURL = "https://kauth.kakao.com/oauth/token";

            try {
            	/***************************************
            	 * 요청을 보낼 값 셋팅 
            	 **************************************/
            	 URL url = new URL(reqURL);
                 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                 conn.setRequestMethod("POST");
                 conn.setDoOutput(true);// POST 요청을 위해 기본값이 false인 setDoOutput을 true로

                 
                 /***************************************
             	 * POST 요청에 필요로 요구하는 파라미터 스트림을 통해 전송 
             	 **************************************/
                 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));
                 StringBuilder sb = new StringBuilder();
                 sb.append("grant_type=authorization_code");
                 sb.append("&client_id=82693748acf2adcab6a974138e807eea");  // 발급받은 key
                 sb.append("&redirect_uri=http://localhost:8080/login");     // 설정해 놓은 경로
                 sb.append("&code=" + authorize_code);
                 bw.write(sb.toString());
                 bw.flush();

                 /***************************************
              	 * 결과 코드가 200이라면 성공 
              	 **************************************/
                 int responseCode = conn.getResponseCode();
                 System.out.println("responseCode : " + responseCode);


                 /***************************************
               	 * 결과 데이터 (JSON타입의 Response) 메세지 읽어오기 
               	 **************************************/
                 BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                 String line = "";
                 String result = "";
                 while ((line = br.readLine()) != null) {
                     result += line;
                 }

                 /***************************************
            	 * Gson 라이브러리에 포함된 클래스로 JSON파싱 객체 생성 
            	 **************************************/
                 JsonParser parser = new JsonParser();
                 JsonElement element = parser.parse(result);

                 /***************************************
             	 * 파싱된 데이터 읽어오기  
             	 **************************************/	
                 access_Token = element.getAsJsonObject().get("access_token").getAsString();
                 refresh_Token = element.getAsJsonObject().get("refresh_token").getAsString();

                 System.out.println("access_token : " + access_Token);
                 System.out.println("refresh_token : " + refresh_Token);

                 
                 /***************************************
              	 * 스트림은 사용 후 꼭 닫아주기! 
              	 **************************************/	
                 br.close();
                 bw.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            return access_Token;
        }
        
        
        public HashMap<String, Object> getUserInfo (String access_Token) {

        	 /***************************************
          	 * 사용자마다 정보가 다를 수 있기에 HashMap타입으로 선언 
          	 **************************************/	
            HashMap<String, Object> userInfo = new HashMap<String, Object>();
            String reqURL = "https://kapi.kakao.com/v2/user/me";
            try {
            	
            	/***************************************
              	 * 요청을 보낼 값 셋팅 
              	 **************************************/
                URL url = new URL(reqURL);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");

                /***************************************
              	 * 요청에 필요한 Header에 포함될 내용 
              	 **************************************/
                conn.setRequestProperty("Authorization", "Bearer " + access_Token);
                
                /***************************************
              	 *결과 코드가 200이라면 성공 
              	 **************************************/
                int responseCode = conn.getResponseCode();
                System.out.println("responseCode : " + responseCode);

                
                /***************************************
              	 *결과 데이터 (JSON타입의 Response) 메세지 읽어오기 
              	 **************************************/
                BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                String line = "";
                String result = "";
                while ((line = br.readLine()) != null) {
                    result += line;
                }
                br.close();
                System.out.println("response body : " + result);

                
                /***************************************
            	 * Gson 라이브러리에 포함된 클래스로 JSON파싱 객체 생성 
            	 **************************************/
                JsonParser parser = new JsonParser();
                JsonElement element = parser.parse(result);
                
                
                /***************************************
             	 * 파싱된 데이터 읽어오기  
             	 **************************************/	
                JsonObject properties = element.getAsJsonObject().get("properties").getAsJsonObject();
                JsonObject kakao_account = element.getAsJsonObject().get("kakao_account").getAsJsonObject();

                String nickname = properties.getAsJsonObject().get("nickname").getAsString();
                String profile_image = properties.getAsJsonObject().get("profile_image").getAsString();
                String email = kakao_account.getAsJsonObject().get("email").getAsString();

                userInfo.put("nickname", nickname);
                userInfo.put("email", email);
                userInfo.put("profile_image", profile_image);
                
                
                
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
               
            }

            return userInfo;
        }
}

 

+ Recent posts