2

I am working on a form where in I am taking the user details like Name, email phone etc. In the same form once all the data is provided, user needs to click on Take photo button, camera gets initiated and I am able to capture the image and display in img tag in the html. Once this all is done, User needs to click on save button. All of these data including the image needs to get saved in the database/models created in backend. I have set my media and static file locations correctly. I am stuck in saving the image. I tried lot of options, but of no help. my model to save data - models.py

class UserDetails(models.Model):
    User_name = models.CharField(max_length= 300)
    User_phone = models.BigIntegerField()
    User_address = models.TextField()
    User_pic = models.FileField(upload_to='documents/%Y/%m/%d')

My HTML form

{% extends 'base.html' %}

{% load static %} {% block content %}

  <div class="container-fluid">
    <div class="row">
     <div class="col-md-8">  
        <div id="accordion" role="tablist">

            <form method="POST" action="/usersave/" enctype="multipart/form-data">
                {% csrf_token %}
                  ....

               <div class="card-body">
                 <div class="row">
                   <div class="col-md-4 ml-auto mr-auto">
                      <div class="form-group">                                
                         <video id="video" autoplay ></video>                               
                         <canvas id="canvas"></canvas>                                
                   </div>
             <button id="startbutton1" class="btn btn-outline-secondary btn-sm">Take Photo</button>
                          <script src="{% static "assets/js/capture.js" %}"></script> 
                        </div>

                        .....
                    <div class="img  pull-center" >                                            
                    <img id ="photo" name="photo" alt="The screen capture will appear in this box.">                   
          </form>
      </div>                
    </div>  
 </div>

Views.py

def usersave(request):
if request.method== 'POST':        
    User_name = request.POST["Username"]
    User_phone = request.POST["Userphone"]
    User_address = request.POST["Useraddress"]
    pic = request.FILES["photo"]
    User_info= UserDetails(User_name=User_name, User_phone=User_phone, User_address=User_address, User_pic= pic)
    User_info.save()    
    return render(request, 'some.html')

Using this capture.js file I am able to take photo and populate the HTML file in img tag

(function() {

var width = 320;    
var height = 0;    
var streaming = false;  
var video = null;
var canvas = null;
var photo = null;
var startbutton1 = null;

function startup() {
  video = document.getElementById('video');
  canvas = document.getElementById('canvas');
  photo = document.getElementById('photo');
  startbutton1 = document.getElementById('startbutton1');

  navigator.mediaDevices.getUserMedia({video: true, audio: false})
  .then(function(stream) {
    video.srcObject = stream;
    video.play();
  })
  .catch(function(err) {
    console.log("An error occurred: " + err);
  });

  video.addEventListener('canplay', function(ev){
    if (!streaming) {
      height = video.videoHeight / (video.videoWidth/width);


      if (isNaN(height)) {
        height = width / (4/3);
      }

      video.setAttribute('width', width);
      video.setAttribute('height', height);
      canvas.setAttribute('width', width);
      canvas.setAttribute('height', height);
      streaming = true;
    }
  }, false);

  startbutton1.addEventListener('click', function(ev){
    takepicture();
    ev.preventDefault();
  }, false);

  clearphoto();
}

function clearphoto() {
  var context = canvas.getContext('2d');
  context.fillStyle = "#AAA";
  context.fillRect(0, 0, canvas.width, canvas.height);

  var data = canvas.toDataURL('image/png');
  photo.setAttribute('src', data);
}

function takepicture() {
  var context = canvas.getContext('2d');
  if (width && height) {
    canvas.width = width;
    canvas.height = height;
    context.drawImage(video, 0, 0, width, height);

    var data = canvas.toDataURL('image/png');
    photo.setAttribute('src', data);
  } else {
    clearphoto();
  }
}
window.addEventListener('load', startup, false);

})();

Take Photo button allows to capture the photo and put it in img tag

Kindly guide me. Thanks

Subodh
  • 21
  • 1
  • 2

1 Answers1

5

I think this answer will be helpful.

Let us start answering from the backend side implementation: In your models.py, I prefer to use the Image field rather than the file field.

class Image(models.Model):
    username = models.CharField(max_length=30)
    image = models.ImageField(upload_to='images')

And first of all, configure your media directory carefully.

Your urls.py module should look like this.

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.image_upload, name='image_upload'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

You should define MEDIA_ROOT and MEDIA_URL in your settings.py.

BASE_DIR = Path(__file__).resolve().parent.parent
TEMPLATE_DIR = os.path.join(BASE_DIR, 'templates')

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

Your views.py should look like this, say the image is uploaded using POST request.

# Import these methods
from django.core.files import File
from django.core.files.base import ContentFile
from django.core.files.temp import NamedTemporaryFile


def image_upload(request):
    context = dict()
    if request.method == 'POST':
        username = request.POST["username"]
        image_path = request.POST["src"]  # src is the name of input attribute in your html file, this src value is set in javascript code
        image = NamedTemporaryFile()
        image.write(urlopen(path).read())
        image.flush()
        image = File(image)
        name = str(image.name).split('\\')[-1]
        name += '.jpg'  # store image in jpeg format
        image.name = name
        if image is not None:
            obj = Image.objects.create(username=username, image=image)  # create a object of Image type defined in your model
            obj.save()
            context["path"] = obj.image.url  #url to image stored in my server/local device
            context["username"] = obj.username
        else :
            return redirect('/')
        return redirect('any_url')
    return render(request, 'index.html', context=context)  # context is like respose data we are sending back to user, that will be rendered with specified 'html file'.         

To check how webcam image data is sent to the server, please refer answer to this question. How can I capture a picture from webcam and store it in a ImageField or FileField in Django?

When an image is captured from a webcam, the source(src) of the input field contains URL data. And at server-side, request.POST["src"] actually gives us URL data, which is the path to that image in temporary space.

Now, It's time to discuss the frontend implementation: To implement Webcam interfacing, please refer this excellent tutorial here.

We use WebRTC API to implement it. Even though there are many ways, I prefer using WebRTC API.

So, your HTML code, will almost look like this:

<!doctype html>
<html>
    {% block content %}
    {% load static %}
    <head>
        <title>VisionApp</title>
        <meta charset='utf-8'>
        <link rel="stylesheet" href="{% static 'css/style.css' %}" type="text/css" media="all">
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <script type="module" src="{% static 'js/index.js' %}"></script>
    </head>
    <body>
        <div class="contentarea">
            <div class="Input">
                <form method="POST" name="inputForm" enctype='multipart/form-data'>
                    {% csrf_token %}
                    <div id="camera" class="camera">
                        <video id="video">Video stream not available.</video>
                        <button id="startbutton" type="button">Take photo</button>
                        <input id="webimg" value="" name="src" type="text" style="display: none;">
                        <canvas id="canvas">
                        </canvas>
                    </div>
                    <br>
                    <div>
                        <img id="photo" alt="your image">
                    </div>
                    <br>
                    <button type="submit" class="button" id="submit">Submit</button>
                </form>
            </div>
           <img src="{{ path }}" alt="The screen capture will appear in this box.">
        </div>
    </body>
    {% endblock %}
</html>

Here src = "{{ path }}", path value comes from context. And value for img attribute with id="photo" is set in javascript.

Here is the CSS code:

#video {
  border: 1px solid black;
  box-shadow: 2px 2px 3px black;
  width:320px;
  height:240px;
}

#photo {
  border: 1px solid black;
  box-shadow: 2px 2px 3px black;
  width:320px;
  height:240px;
}

#canvas {
  display:none;
}

.camera {
  width: 340px;
  display:inline-block;
}

.output {
  width: 340px;
  display:inline-block;
}

#startbutton {
  display:block;
  position:relative;
  margin-left:auto;
  margin-right:auto;
  bottom:32px;
  background-color: rgba(0, 150, 0, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.7);
  box-shadow: 0px 0px 1px 2px rgba(0, 0, 0, 0.2);
  font-size: 14px;
  font-family: "Lucida Grande", "Arial", sans-serif;
  color: rgba(255, 255, 255, 1.0);
}

.contentarea {
  font-size: 16px;
  font-family: "Lucida Grande", "Arial", sans-serif;
  width: 760px;
}

Now, finally, your javascript file may look like this:

(function() {
  // The width and height of the captured photo. We will set the
  // width to the value defined here, but the height will be
  // calculated based on the aspect ratio of the input stream.

  var width = 320;    // We will scale the photo width to this
  var height = 0;     // This will be computed based on the input stream

  // |streaming| indicates whether or not we're currently streaming
  // video from the camera. Obviously, we start at false.

  var streaming = false;

  // The various HTML elements we need to configure or control. These
  // will be set by the startup() function.

  var video = null;
  var canvas = null;
  var photo = null;
  var startbutton = null;

  function startup() {
    video = document.getElementById('video');
    canvas = document.getElementById('canvas');
    photo = document.getElementById('photo');
    startbutton = document.getElementById('startbutton');

    navigator.mediaDevices.getUserMedia({video: true, audio: false})
    .then(function(stream) {
      video.srcObject = stream;
      video.play();
    })
    .catch(function(err) {
      console.log("An error occurred: " + err);
    });

    video.addEventListener('canplay', function(ev){
      if (!streaming) {
        height = video.videoHeight / (video.videoWidth/width);
      
        // Firefox currently has a bug where the height can't be read from
        // the video, so we will make assumptions if this happens.
      
        if (isNaN(height)) {
          height = width / (4/3);
        }
      
        video.setAttribute('width', width);
        video.setAttribute('height', height);
        canvas.setAttribute('width', width);
        canvas.setAttribute('height', height);
        streaming = true;
      }
    }, false);

    startbutton.addEventListener('click', function(ev){
      takepicture();
      ev.preventDefault();
    }, false);
    
    clearphoto();
  }

  // Fill the photo with an indication that none has been
  // captured.

  function clearphoto() {
    var context = canvas.getContext('2d');
    context.fillStyle = "#AAA";
    context.fillRect(0, 0, canvas.width, canvas.height);

    var data = canvas.toDataURL('image/png');
    photo.setAttribute('src', data);
  }
  
  // Capture a photo by fetching the current contents of the video
  // and drawing it into a canvas, then converting that to a PNG
  // format data URL. By drawing it on an offscreen canvas and then
  // drawing that to the screen, we can change its size and/or apply
  // other changes before drawing it.

  function takepicture() {
    var context = canvas.getContext('2d');
    if (width && height) {
      canvas.width = width;
      canvas.height = height;
      context.drawImage(video, 0, 0, width, height);
    
      var data = canvas.toDataURL('image/png');
      photo.setAttribute('src', data);
    } else {
      clearphoto();
    }
  }

  // Set up our event listener to run the startup process
  // once loading is complete.
  window.addEventListener('load', startup, false);
})();

Hope these code snippets will help you! Thank You!

Namballa Mukesh
  • 174
  • 3
  • 15
  • thank you , can we capture multiple images , and upload them to the database please ? – artiest Sep 30 '21 at 12:40
  • @HunarMohammed can you give an example? capturing multiple images in the sense, you are trying to add multiple buttons for capturing images for different purposes? – Namballa Mukesh Oct 03 '21 at 21:27
  • 2
    I had to make some changes to make this work. You have input called webimg which is not used, I gave it the name webimg and set the value in the javascript at the same place teh src of photo is set. Then in the view I pulled the webimg with `webimg = urlopen(request.POST["webimg"])` then converted it into a byte string with `data = webimg.read()` and wrapped it in a ContentFile `content_file = ContentFile( data, "webcam.jpg" )` This I was then able to assign to the model. – Joshua Nov 23 '22 at 02:33