본문 바로가기

Data-science/deep learning

[레이블링 툴] html, flask, python, javascript로 만드는 딥러닝 레이블링 툴 기본

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!