NVIDIA - Inpainting Demo (nvidia.com)

NVIDIA 上线的一个基于 AI 算法模型的图像修复测试网站. 如图,

网站 pages 文件夹有三个js:ImageMasking.js、ImageSelection.js 和 InpaintingResult.js,其中,ImageMasking.js 内容如下:

import React, { Component } from 'react'
import { Link, Redirect } from 'react-router-dom'
import { isMobile } from 'react-device-detect'
import Modal from 'react-modal'
import { Button } from '@nvidia/kaizen-ui'
import { Subscribe } from 'unstated'
import { AppContainer } from '../../containers'
import { Article, Slider } from '../../components'
import './image-masking.css'

class ImageMaskingComponent extends Component {
  constructor(props) {
    super(props)

    this.state = {
      brushSize: 35,
      isOpen: false,
      showMask: true
    }

    this.isMouseDown = false
    this.canvasWidth = isMobile ? 347 : 512
    this.canvasHeight = isMobile ? 347 : 512
  }

  componentDidMount = () => {
    this.linesArray = []
    this.startDrawIdx = []
    if (this.props.linesArray.length > 0) {
      this.linesArray = this.props.linesArray
      this.startDrawIdx = this.props.startDrawIdx
      this.props.setLinesArray([])
      this.redrawCanvas()
    }
  }

  getMousePos = e => {
    const rect = this.canvasMask.getBoundingClientRect()

    let clientX = e.clientX
    let clientY = e.clientY

    if (e.touches && e.touches.length > 0) {
      clientX = e.touches[0].clientX
      clientY = e.touches[0].clientY
    }

    return {
      x: clientX - rect.left,
      y: clientY - rect.top
    }
  }

  drawLine = (line, color = '#FFF') => {
    this.ctxMask.strokeStyle = color
    this.ctxMask.lineWidth = line.size
    this.ctxMask.lineCap = 'round'
    this.ctxMask.beginPath()
    this.ctxMask.moveTo(line.startX, line.startY)
    this.ctxMask.lineTo(line.endX, line.endY)
    this.ctxMask.stroke()
  }

  drawStart = e => {
    this.isMouseDown = true
    this.startDrawIdx.push(this.linesArray.length)

    const { x, y } = this.getMousePos(e)
    this.x = x
    this.y = y

    this.draw(e)
  }

  drawEnd = () => this.isMouseDown = false

  draw = e => {
    if (!this.isMouseDown) return

    const { x, y } = this.getMousePos(e)
    const newX = x + 1
    const newY = y + 1

    const line = {
      size: this.state.brushSize,
      startX: this.x,
      startY: this.y,
      endX: newX,
      endY: newY
    }

    this.drawLine(line)
    this.linesArray.push(line)
    this.x = newX
    this.y = newY
  }

  clearCanvas = () => this.ctxMask.clearRect(0, 0, this.canvasWidth, this.canvasHeight)

  redrawCanvas = color => this.linesArray.forEach((line, idx) => this.drawLine(line, color))

  handleBrushSizeChange = (event) => this.setState({ brushSize: parseInt(event.target.value, 10) })

  uploadNewImage = () => this.props.setSelectedImage('')

  applyModel = () => {
    // Save the mask array.
    this.props.setLinesArray(this.linesArray)
    this.props.setStartDrawIdx(this.startDrawIdx)

    // Save the mask as show to the user.
    const imageMask = this.canvasMask.toDataURL()
    this.props.setImageMask(imageMask)

    // Process and save the mask to create one for the inference model.
    this.clearCanvas()
    this.ctxMask.fillStyle = '#FFF'
    this.ctxMask.fillRect(0, 0, this.canvasWidth, this.canvasHeight)
    this.redrawCanvas('#000')
    const processedImageMask = this.canvasMask.toDataURL()
    this.props.setProcessedImageMask(processedImageMask)

    // Send the request.
    this.sendRequest(processedImageMask)
    this.openModal()
  }

  // POST 请求图像修复API
  sendRequest = (processedImageMask) => {
    var self = this
    var data = new FormData()
    data.append('original-image-file', this.dataURItoBlob(this.props.selectedImage))
    data.append('masked-image-file', this.dataURItoBlob(processedImageMask))
    fetch(`${process.env.REACT_APP_SERVICE_ENDPOINT || 'http://52.26.183.137:8080'}/v1/partialconv/inpainting`, {
      method: 'POST',
      body: data
    }).then((response) => response.json())
      .then((body) => {
        self.props.setResultImage(body.formattedResultImageUrl)
        self.props.setRequestId(body.requestId)
        self.props.setRequestComplete(true)
      })
  }

  dataURItoBlob = (dataURI) => {
    var byteString = atob(dataURI.split(',')[1]);
    var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
    var ab = new ArrayBuffer(byteString.length);
    var ia = new Uint8Array(ab);
    for (var i = 0; i < byteString.length; i++) {
      ia[i] = byteString.charCodeAt(i);
    }
    var blob = new Blob([ab], { type: mimeString });
    return blob;
  }

  clear = () => {
    this.clearCanvas()
    this.linesArray = []
    this.startDrawIdx = []
  }

  undo = () => {
    if (this.startDrawIdx.length > 0) {
      this.linesArray.splice(
        this.startDrawIdx.pop()
      )
      this.clearCanvas()
      this.redrawCanvas()
    }
  }

  openModal = () => this.setState({ showModal: true })
  closeModal = () => this.setState({ showModal: false })
  toggleMask = () => this.setState({ showMask: !this.state.showMask})

  render() {
    const canvasClass = !this.state.showMask ? 'mask-canvas hidden' : 'mask-canvas'
    return (
      <Article className='image-masking'>
        <h2>Step Two</h2>
        <span className='subheader'>Use your cursor to mask out any portions of your image,<br />and select Apply Model to view your results.</span>
        <div className='canvas'>
          <img src={this.props.selectedImage} title='background' alt='background' className='background' />
          <canvas
            id='#canvas'
            width={this.canvasWidth}
            height={this.canvasHeight}
            ref={canvas => {
              if (canvas) {
                this.canvasMask = canvas
                this.ctxMask = canvas.getContext('2d')
              }
            }}
            onMouseDown={this.drawStart}
            onClick={() => false}
            onMouseUp={this.drawEnd}
            onMouseOut={this.drawEnd}
            onMouseMove={this.draw}
            onTouchStart={this.drawStart}
            onTouchMove={this.draw}
            onTouchEnd={this.drawEnd}
            onTouchCancel={this.drawEnd}
            className={canvasClass}
          />
        </div>
        <Slider
          label='Brush Width:'
          value={this.state.brushSize}
          onChange={this.handleBrushSizeChange}
          step='1'
          min='1'
          max='100'
        />
        <Button.Group className='masking-controls'>
          <Button as='div'>
            <Link to='selection' onClick={this.uploadNewImage}>Change Image</Link>
          </Button>
          <Button onClick={this.toggleMask}>
            {this.state.showMask ? 'Hide Mask' : 'Show Mask'}
          </Button>
          <Button onClick={this.clear}>
            Clear
          </Button>
          <Button onClick={this.undo}>
            Undo
          </Button>
          <Button primary onClick={this.applyModel} className='apply-model-button'>
            Apply Model
          </Button>
        </Button.Group>
        <Modal
          isOpen={this.state.showModal}
          className='ui modal'
          overlayClassName='ui overlay'
        >
          <h2 className='title'>Creating Inpainted Image</h2>
          <p>Analyzing masks...</p>
          <div className='loading-bar' />
        </Modal>
      </Article>
    )
  }
}

export const ImageMasking = () =>
  <Subscribe to={[AppContainer]}>
    {container => (
      container.state.requestComplete
      ? <Redirect to='result' />
      : <ImageMaskingComponent {...container.state} {...container} />
    )}
  </Subscribe>



// WEBPACK FOOTER //
// ./src/pages/ImageMasking/ImageMasking.js
Last modification:June 18th, 2021 at 01:52 pm