728x90
html 기반으로 이미지위에 사각형이나 그림을 그리고 이를 저장하려면 어떻게 해야할까? 지금부터 그 방법을 소개한다.
필요도구 : javascript, d3.js. html, css
base.html은 index navigator 역할을 한다.
<!DOCTYPE HTML>
<head lang="kr">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap 4</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<style>
p { margin:20px 0px; }
</style>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>
<script
src="https://code.jquery.com/jquery-3.6.0.js"
integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk="
crossorigin="anonymous"
></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<style>
body {font-family: "Lato", sans-serif}
.mySlides {display: none}
</style>
</head>
<body>
<header>
<!-- Navbar -->
<div class="w3-top">
<div class="w3-bar w3-black w3-card">
<a class="w3-bar-item w3-button w3-padding-large w3-hide-medium w3-hide-large w3-right" href="javascript:void(0)" onclick="myFunction()" title="Toggle Navigation Menu"><i class="fa fa-bars"></i></a>
<a href="{{url_for('main')}}" class="w3-bar-item w3-button w3-padding-large">Main</a>
<a href="{{url_for('hole_reg')}}" class="w3-bar-item w3-button w3-padding-large w3-hide-small">Hole Reg.</a>
<a href="{{url_for('threshold_config')}}" class="w3-bar-item w3-button w3-padding-large w3-hide-small">Threshold Config.</a>
</div>
</div>
</header>
<main role="main">
<!-- Main content block -->
{% block content %}{% endblock %}
</main>
</body>
</html>
위 코드에서 중요한 부분은 이 부분 d3를 활용한다는 것!
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
d3 를 활용하면 rectangle을 그릴 수 있다.
{% extends 'base.html' %}
{% block content %}
<html>
<head>
<!--<script async src="http://jsfiddle.net/vcaz7rt1/embed/"></script>-->
<!--<script async src="https://jsfiddle.net/SunboX/vj4jtdg8/embed/"></script>-->
<style>
svg {
border: solid 1px red;
background: url('/static/img/1.png');
}
rect {
fill: white;
stroke: red;
stroke-width: 2px;
}
</style>
</head>
<body>
<br><br>
<h4>Drag the mouse to create a rectangle</h4>
<button class="btn btn-primary" id='rectangle'>Rectangle</button>
<button class="btn btn-primary" id='saveButton'>이미지 저장하기</button>
<!--<a class="btn btn-primary" id="link">이미지 저장하기</a>-->
</body>
<!--<script src="https://rawgit.com/exupero/saveSvgAsPng/gh-pages/saveSvgAsPng.js"></script>-->
<script src="https://cdn.rawgit.com/eligrey/canvas-toBlob.js/f1a01896135ab378aa5c0118eadd81da55e698d8/canvas-toBlob.js"></script>
<script src="https://cdn.rawgit.com/eligrey/FileSaver.js/e9d941381475b5df8b7d7691013401e171014e89/FileSaver.min.js"></script>
<script>
d3.select('#rectangle').on('click', function(){
console.log('clicked');
new Rectangle();
});
var w = 1080, h = 1400;
var width = 1080, height = 1400;
var svg = d3.select('body').append('svg').attr({width: w, height: h});
function Rectangle() {
var self = this, rect, rectData = [], isDown = false, m1, m2, isDrag = false;
svg.on('mousedown', function() {
m1 = d3.mouse(this);
if (!isDown && !isDrag) {
self.rectData = [ { x: m1[0], y: m1[1] }, { x: m1[0], y: m1[1] } ];
self.rectangleElement = d3.select('svg').append('rect').attr('class', 'rectangle').call(dragR);
self.pointElement1 = d3.select('svg').append('circle').attr('class', 'pointC').call(dragC1);
self.pointElement2 = d3.select('svg').append('circle').attr('class', 'pointC').call(dragC2);
self.pointElement3 = svg.append('circle').attr('class', 'pointC').call(dragC3);
self.pointElement4 = svg.append('circle').attr('class', 'pointC').call(dragC4);
updateRect();
isDrag = false;
} else {
isDrag = true;
}
isDown = !isDown;
})
.on('mousemove', function() {
m2 = d3.mouse(this);
if(isDown && !isDrag) {
self.rectData[1] = { x: m2[0], y: m2[1] };
updateRect();
}
});
function updateRect() {
rect = d3.select(self.rectangleElement[0][0]);
rect.attr({
x: self.rectData[1].x - self.rectData[0].x > 0 ? self.rectData[0].x : self.rectData[1].x,
y: self.rectData[1].y - self.rectData[0].y > 0 ? self.rectData[0].y : self.rectData[1].y,
width: Math.abs(self.rectData[1].x - self.rectData[0].x),
height: Math.abs(self.rectData[1].y - self.rectData[0].y)
});
var point1 = d3.select(self.pointElement1[0][0]).data(self.rectData);
point1.attr('r', 5)
.attr('cx', self.rectData[0].x)
.attr('cy', self.rectData[0].y);
var point2 = d3.select(self.pointElement2[0][0]).data(self.rectData);
point2.attr('r', 5)
.attr('cx', self.rectData[1].x)
.attr('cy', self.rectData[1].y);
var point3 = d3.select(self.pointElement3[0][0]).data(self.rectData);
point3.attr('r', 5)
.attr('cx', self.rectData[1].x)
.attr('cy', self.rectData[0].y);
var point3 = d3.select(self.pointElement4[0][0]).data(self.rectData);
point3.attr('r', 5)
.attr('cx', self.rectData[0].x)
.attr('cy', self.rectData[1].y);
}
var dragR = d3.behavior.drag().on('drag', dragRect);
function dragRect() {
var e = d3.event;
for(var i = 0; i < self.rectData.length; i++){
d3.select(self.rectangleElement[0][0])
.attr('x', self.rectData[i].x += e.dx )
.attr('y', self.rectData[i].y += e.dy );
}
rect.style('cursor', 'move');
updateRect();
}
var dragC1 = d3.behavior.drag().on('drag', dragPoint1);
var dragC2 = d3.behavior.drag().on('drag', dragPoint2);
var dragC3 = d3.behavior.drag().on('drag', dragPoint3);
var dragC4 = d3.behavior.drag().on('drag', dragPoint4);
function dragPoint1() {
var e = d3.event;
d3.select(self.pointElement1[0][0])
.attr('cx', function(d) { return d.x += e.dx })
.attr('cy', function(d) { return d.y += e.dy });
updateRect();
}
function dragPoint2() {
var e = d3.event;
d3.select(self.pointElement2[0][0])
.attr('cx', self.rectData[1].x += e.dx )
.attr('cy', self.rectData[1].y += e.dy );
updateRect();
}
function dragPoint3() {
var e = d3.event;
d3.select(self.pointElement3[0][0])
.attr('cx', self.rectData[1].x += e.dx )
.attr('cy', self.rectData[0].y += e.dy );
updateRect();
}
function dragPoint4() {
var e = d3.event;
d3.select(self.pointElement4[0][0])
.attr('cx', self.rectData[0].x += e.dx )
.attr('cy', self.rectData[1].y += e.dy );
updateRect();
}
}//end Rectangle
// Set-up the export button
d3.select('#saveButton').on('click', function(){
var svgString = getSVGString(svg.node());
svgString2Image( svgString, 2*width, 2*height, 'png', save ); // passes Blob and filesize String to the callback
function save( dataBlob, filesize ){
saveAs( dataBlob, 'D3 vis exported to PNG.png' ); // FileSaver.js function
}
});
// Below are the functions that handle actual exporting:
// getSVGString ( svgNode ) and svgString2Image( svgString, width, height, format, callback )
function getSVGString( svgNode ) {
svgNode.setAttribute('xlink', 'http://www.w3.org/1999/xlink');
var cssStyleText = getCSSStyles( svgNode );
appendCSS( cssStyleText, svgNode );
var serializer = new XMLSerializer();
var svgString = serializer.serializeToString(svgNode);
svgString = svgString.replace(/(\w+)?:?xlink=/g, 'xmlns:xlink='); // Fix root xlink without namespace
svgString = svgString.replace(/NS\d+:href/g, 'xlink:href'); // Safari NS namespace fix
return svgString;
function getCSSStyles( parentElement ) {
var selectorTextArr = [];
// Add Parent element Id and Classes to the list
selectorTextArr.push( '#'+parentElement.id );
for (var c = 0; c < parentElement.classList.length; c++)
if ( !contains('.'+parentElement.classList[c], selectorTextArr) )
selectorTextArr.push( '.'+parentElement.classList[c] );
// Add Children element Ids and Classes to the list
var nodes = parentElement.getElementsByTagName("*");
for (var i = 0; i < nodes.length; i++) {
var id = nodes[i].id;
if ( !contains('#'+id, selectorTextArr) )
selectorTextArr.push( '#'+id );
var classes = nodes[i].classList;
for (var c = 0; c < classes.length; c++)
if ( !contains('.'+classes[c], selectorTextArr) )
selectorTextArr.push( '.'+classes[c] );
}
// Extract CSS Rules
var extractedCSSText = "";
for (var i = 0; i < document.styleSheets.length; i++) {
var s = document.styleSheets[i];
try {
if(!s.cssRules) continue;
} catch( e ) {
if(e.name !== 'SecurityError') throw e; // for Firefox
continue;
}
var cssRules = s.cssRules;
for (var r = 0; r < cssRules.length; r++) {
if ( contains( cssRules[r].selectorText, selectorTextArr ) )
extractedCSSText += cssRules[r].cssText;
}
}
return extractedCSSText;
function contains(str,arr) {
return arr.indexOf( str ) === -1 ? false : true;
}
}
function appendCSS( cssText, element ) {
var styleElement = document.createElement("style");
styleElement.setAttribute("type","text/css");
styleElement.innerHTML = cssText;
var refNode = element.hasChildNodes() ? element.children[0] : null;
element.insertBefore( styleElement, refNode );
}
}
function svgString2Image( svgString, width, height, format, callback ) {
var format = format ? format : 'png';
var imgsrc = 'data:image/svg+xml;base64,'+ btoa( unescape( encodeURIComponent( svgString ) ) ); // Convert SVG string to data URL
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
var image = new Image();
image.onload = function() {
context.clearRect ( 0, 0, width, height );
context.drawImage(image, 0, 0, width, height);
canvas.toBlob( function(blob) {
var filesize = Math.round( blob.length/1024 ) + ' KB';
if ( callback ) callback( blob, filesize );
});
};
image.src = imgsrc;
}
</script>
</html>
{% endblock %}
저장을 누르면 다음과 같이 저장된다!!
몇시간 삽질했더라.... 지인의 조언을 얻지 못했으면 몇일은 헤맸을 텐데..
하루삽질로 그침.
이 글을 보는 누군가는 행복하길!
키워드는 d3!
'Data-science > deep learning' 카테고리의 다른 글
[tensorflow] 메모리 과부하시 해결법 (0) | 2022.01.07 |
---|---|
[논문 읽기] UIS-RNN 설명, FULLY SUPERVISED SPEAKER DIARIZATION 설명 - 1 (4) | 2021.12.27 |
[error handling] python generator, fit_generator를 썼을 때 loss가 변하지 않고 학습이 되지 않을 때, 시계열 데이터 학습 실수 (0) | 2021.12.01 |
[논문 읽기] EfficientDet: Scalable and Efficient Object Detection (0) | 2021.11.18 |
[torch] RuntimeError("{} is a zip archive 에러 해결 방법 (0) | 2021.11.08 |