[실습] 크롤링 + Teacherable machine 활용 = Web

Joy·2020년 4월 12일
2

[조코딩] 완성형 서비스 만들기 - 2
https://velog.io/@joygoround/%EC%A1%B0%EC%BD%94%EB%94%A9-%EC%99%84%EC%84%B1%ED%98%95-%EC%84%9C%EB%B9%84%EC%8A%A4-%EB%A7%8C%EB%93%A4%EA%B8%B0-2
를 참고해, 직접 사진 인식하는 웹 만들기

서비스 기획

닮은 동물상 판별 서비스 대신 다른 서비스 기획하기로 함.
요즘 인기 있는 드라마인 <슬기로운 의사생활>의 다섯 주인공 닮은 정도 판별하기로 함.

이미지 저장

다섯 인물이니 각각 폴더를 만들고 teacherable machine 이 학습할 수 있는 이미지 데이터를 모아야 함.
완성형 서비스 만들기 1강에서 만든 프로그램으로 네이버에서 필요한 이미지(각 30개)를 클롤링해 저장함.

teacherable machine으로 모델만들기

https://teachablemachine.withgoogle.com/ 접속

get started > image project 에서 인물별로 클래스로 만들어서 학습대상 이미지데이터 업로드 & 데이터 클랜징(얼굴 인식에 적합하지 않은 이미지 등 삭제)

사진으로 계속 테스트 하면서 얼굴이 잘 나와있는 사진 계속 추가해서 정확도 높이기 - 90% 이상일때까지 반복.

모델사용하기

클라우드 상에 업로드 - 링크타고 접근 가능

업로드 누르면 링크 생성
https://teachablemachine.withgoogle.com/models/loVFZUvDd/

웹 환경 열고 코드 붙여넣기

구름 IDE 사용

스텍은 hmtl css js 로 새 컨테이너 만들기

html body에 복붙하고 페이지 열어보기


원래 코드로는 이런식으로 웹캠이 열림.

웹캠 대신 이미지 넣는 기능 추가하기

Codepen 에서 검색하기 codepen.io

적절한 코드 찾고 라이센스 확인 - 복사해서 html에 주석으로 붙여주기

html 전체 코드 복사해서 원 코드 start 버튼 아리애 붙이기

++ 지저분한 코드 정렬 : 마우스 오른> 코드 포메팅> html 포맷
or Alt+shift+P : 자동정렬

css도 복사 > style.css 파일 비우고 붙이기

js 코드는 html 하단에 다른 스크립트랑 넣어주기

요거로 감싸서

->> 페이지를 보면 웹캠 위에 이미지 첨부할 수 있는 파트가
있는걸 알 수 있음.

웹캠 인식하는 코드 바꾸기

loop, 웹켐인식(화면에 웹캠을 표시하는 코드도 지워) 등 불필요한 코드 지우고
predict 부분에 웹캠대신 이미지(임시로 한글로)

predict부분 동작하는 코드 보기 위해서 검색 : "teachable machine document" 검색해서 공식 깃허브 찾기- MOdel - predict 부분 설명

변수를 만들고 이미지 코드를 가져옴

var image = document.getElementById("face-image") 

이르케 하고 face-image 를 복사하고
웹페이지 개발자 도구에서 이미지를 없로드 했을 때 해당 이미지 부분 코드 (class = "file-upload-image" )을 찾고 (ctrl + F) id 값 추가 ->> 이러면 id를 통해서 hmtl 엘리먼트를 선택해서 가져올 수 있음.

<img class = "file-upload-image" id = "face-image">

"이미지"자리에 만들어 준 var image 를 predict에 넣기

++ 다큐먼트에서 flipped 여부가 있었는데 안뒤집혔으니까 함수의 두번째 인자로 flipped 가 있었는데 false로 하고 저장

const predicton = await model.predict(image, false);

predict() 를 실행해 줄 버튼 따로 만들기

-- 원래는 루프 돌면서 계속 실행하는 거였는데 루프가 없으니까
init 하는 스타트 버튼 복사해서 밑에 복붙하고
init 대신 predict() 넣어줌. Start라는 이름 대신 Predict로 바꿔줌.

확인

https://hospital-playlist-jpyva.run.goorm.io/hospital_playlist/index.html

index.html:62 Uncaught (in promise) TypeError: document.getElecmentByID is not a function
    at predict (index.html:62)
    at HTMLButtonElement.onclick (index.html:14)
predict @ index.html:62
onclick @ index.html:14

에러 발생 : 코드에 오타

수정 후 재실행

후기

티처블 머신 자체는 이미지 전체를 인식하는 거기 때문에 정확한 얼굴인식을 위해서는 다른 모델 만들 필요있음.
손쉽게 모델을 만들고 가져다 쓸 수 있어서 활용도가 높을 것 같음

url

https://hospital-playlist-jpyva.run.goorm.io/hospital_playlist/index.html

코드

html

<!DOCTYPE html>
<html>

<head>
	<link rel="stylesheet" href="style.css">
</head>

<body>
	<div class="title">
		Hello, goorm!
	</div>
	<div>Teachable Machine Image Model</div>
	<button type="button" onclick="init()">Start</button>
	<button type="button" onclick="predict()">Predict</button>
	<script class="jsbin" src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
	<div class="file-upload">
		<button class="file-upload-btn" type="button" onclick="$('.file-upload-input').trigger( 'click' )">Add Image</button>

		<div class="image-upload-wrap">
			<input class="file-upload-input" type='file' onchange="readURL(this);" accept="image/*" />
			<div class="drag-text">
				<h3>Drag and drop a file or select add Image</h3>
			</div>
		</div>
		<div class="file-upload-content">
			<img class="file-upload-image" id="face-image" src="#" alt="your image" />
			<div class="image-title-wrap">
				<button type="button" onclick="removeUpload()" class="remove-image">Remove
					<span class="image-title">Uploaded Image</span>
				</button>
			</div>
		</div>
	</div>

	<div id="label-container"></div>
	<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.3.1/dist/tf.min.js"></script>
	<script src="https://cdn.jsdelivr.net/npm/@teachablemachine/image@0.8/dist/teachablemachine-image.min.js"></script>
	<script type="text/javascript">
		// More API functions here:
		// https://github.com/googlecreativelab/teachablemachine-community/tree/master/libraries/image
		// the link to your model provided by Teachable Machine export panel
		const URL = "https://teachablemachine.withgoogle.com/models/loVFZUvDd/";
		let model, labelContainer, maxPredictions;
		// Load the image model and setup the webcam
		async function init() {
			const modelURL = URL + "model.json";
			const metadataURL = URL + "metadata.json";
			// load the model and metadata
			// Refer to tmImage.loadFromFiles() in the API to support files from a file picker
			// or files from your local hard drive
			// Note: the pose library adds "tmImage" object to your window (window.tmImage)
			model = await tmImage.load(modelURL, metadataURL);
			maxPredictions = model.getTotalClasses();
			labelContainer = document.getElementById("label-container");
			for (let i = 0; i < maxPredictions; i++) { // and class labels
				labelContainer.appendChild(document.createElement("div"));
			}
		}
		// run the webcam image through the image model
		async function predict() {
			// predict can take in an image, video or canvas html element
			var image = document.getElementById("face-image");
			const prediction = await model.predict(image, false);
			for (let i = 0; i < maxPredictions; i++) {
				const classPrediction =
					prediction[i].className + ": " + prediction[i].probability.toFixed(2);
				labelContainer.childNodes[i].innerHTML = classPrediction;
			}
		}
	</script>
	<script>
		function readURL(input) {
			if (input.files && input.files[0]) {
				var reader = new FileReader();
				reader.onload = function(e) {
					$('.image-upload-wrap').hide();
					$('.file-upload-image').attr('src', e.target.result);
					$('.file-upload-content').show();
					$('.image-title').html(input.files[0].name);
				};
				reader.readAsDataURL(input.files[0]);
			} else {
				removeUpload();
			}
		}

		function removeUpload() {
			$('.file-upload-input').replaceWith($('.file-upload-input').clone());
			$('.file-upload-content').hide();
			$('.image-upload-wrap').show();
		}
		$('.image-upload-wrap').bind('dragover', function() {
			$('.image-upload-wrap').addClass('image-dropping');
		});
		$('.image-upload-wrap').bind('dragleave', function() {
			$('.image-upload-wrap').removeClass('image-dropping');
		});
	</script>>

</body>

<!-- Copyright (c) 2020 by Aaron Vanston (https://codepen.io/aaronvanston/pen/yNYOXR)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -->

</html>

css

body {
	font-family: sans-serif;
	background-color: #eeeeee;
}
.file-upload {
	background-color: #ffffff;
	width: 600px;
	margin: 0 auto;
	padding: 20px;
}
.file-upload-btn {
	width: 100%;
	margin: 0;
	color: #fff;
	background: #1fb264;
	border: none;
	padding: 10px;
	border-radius: 4px;
	border-bottom: 4px solid #15824b;
	transition: all 0.2s ease;
	outline: none;
	text-transform: uppercase;
	font-weight: 700;
}
.file-upload-btn:hover {
	background: #1aa059;
	color: #ffffff;
	transition: all 0.2s ease;
	cursor: pointer;
}
.file-upload-btn:active {
	border: 0;
	transition: all 0.2s ease;
}
.file-upload-content {
	display: none;
	text-align: center;
}
.file-upload-input {
	position: absolute;
	margin: 0;
	padding: 0;
	width: 100%;
	height: 100%;
	outline: none;
	opacity: 0;
	cursor: pointer;
}
.image-upload-wrap {
	margin-top: 20px;
	border: 4px dashed #1fb264;
	position: relative;
}
.image-dropping,
.image-upload-wrap:hover {
	background-color: #1fb264;
	border: 4px dashed #ffffff;
}
.image-title-wrap {
	padding: 0 15px 15px 15px;
	color: #222;
}
.drag-text {
	text-align: center;
}
.drag-text h3 {
	font-weight: 100;
	text-transform: uppercase;
	color: #15824b;
	padding: 60px 0;
}
.file-upload-image {
	max-height: 200px;
	max-width: 200px;
	margin: auto;
	padding: 20px;
}
.remove-image {
	width: 200px;
	margin: 0;
	color: #fff;
	background: #cd4535;
	border: none;
	padding: 10px;
	border-radius: 4px;
	border-bottom: 4px solid #b02818;
	transition: all 0.2s ease;
	outline: none;
	text-transform: uppercase;
	font-weight: 700;
}
.remove-image:hover {
	background: #c13b2a;
	color: #ffffff;
	transition: all 0.2s ease;
	cursor: pointer;
}
.remove-image:active {
	border: 0;
	transition: all 0.2s ease;
}
profile
roundy

0개의 댓글