jhuckaby / webcamjs

HTML5 Webcam Image Capture Library with Flash Fallback
MIT License
2.5k stars 1.11k forks source link

Wrong aspect ratio read from camera #129

Open borisreitman opened 8 years ago

borisreitman commented 8 years ago

This is the basic demo https://pixlcore.com/demos/webcamjs/demos/basic.html

I am using Macbook Pro. As you see that actual camera is shooting it a different aspect ratio. This would be half the trouble -- but the resulting image is wrong aspect ratio, as the real camera image is deformed into 320x240 when making the snapshot.

screen shot 2015-12-15 at 11 08 43 am

jhuckaby commented 8 years ago

Not sure what causes this. Please try these other libraries to see if the bug is WebcamJS specific:

borisreitman commented 8 years ago

Here are the screenshots:

First, another screenshot from webcamjs demo page, for comparison. It shows that the snapshot is squished.

screen shot 2015-12-15 at 11 45 56 am

HTML5 Webcam test page: https://simpl.info/getusermedia/

screen shot 2015-12-15 at 11 45 12 am

Flash Webcam test page: https://www.onlinemictest.com/webcam-test-in-adobe-flash

screen shot 2015-12-15 at 11 44 49 am

jQuery Webcam: http://www.xarg.org/project/jquery-webcam-plugin/ This is the first of the demos that also does a snapshot. You see that the snapshot is correct size.

screen shot 2015-12-15 at 11 44 31 am

Finally, JpegCamera: https://github.com/amw/jpeg_camera Also does a snapshot correctly. You can also see reported camera resolution of 960x540

screen shot 2015-12-15 at 11 43 46 am

jhuckaby commented 8 years ago

Thank you for all these screenshots. Apparently WebcamJS has a bug, whereas all the others don't. I'll look into this as soon as I have time. Thanks!

jhuckaby commented 8 years ago

@borisreitman I cannot reproduce this on any of my 3 MacBooks (2011 15", 2012 Retina 15", and 2013 Air 13"), all using the latest Chrome, and the built-in webcams.

Can you please provide me with:

Thanks!

borisreitman commented 8 years ago

Hi,

Strange, I spoke with someone on Skype and shared my screen (not video) and I think it caused fixing of the webcam. Now if I load your demo page, the camera starts at the long aspect ratio, as in my screenshot, but and then switches to a 320x240 ratio. And the snapshot is also correct.

I have version 10.10.4 (14E46) OSX Yosemite, MacBook Pro model A1278 I am using the built-in webcam.

I used Chrome browser in all screenshots. Version 47.0.2526.80 (64-bit)

I am going to try to get back into the bugged state.

borisreitman commented 8 years ago

I have restarted the computer but can't reproduce it. However, if I visit https://amw.github.io/jpeg_camera/demo/ I can see that my camera is at resolution of 1280x720

screen shot 2015-12-15 at 12 29 44 pm

Therefore I conclude that webcamjs library doesn't read camera resolution, so that if some software places it into a weird resolution that I had of 960x540 (as my previous screenshot shows) then the library can't cope with it.

jhuckaby commented 8 years ago

Wow, that is really bizarre. Yeah, you are correct, WebcamJS does not read the camera's native resolution (honestly I didn't even know you could), and all my test pages "force" it into a hard-coded size, usually 4:3 aspect ratio. Perhaps I should rethink that.

In my experience all the cameras I have tested automatically adjust to fit the aspect ratio in the page without distortion, but I see that is definitely not always the case.

Thanks for your help on this. I'll keep an eye on it, and think about redesigning the library to automatically adjust for the camera's native res.

borisreitman commented 8 years ago

Looking at jpeg_cam source, they get this by reading videoWidth and videoHeight attributes of the video DOM element.

From jpeg_cam source jpeg_camera_html5.coffee:

254     _wait_for_video_ready: ->
255       video_width = parseInt @video.videoWidth
256       video_height = parseInt @video.videoHeight
...
272         @_prepared(@video_width, @video_height)

And in jpeg_camera.coffee

361   _prepared: (video_width, video_height) ->
362     @video_width = video_width
363     @video_height = video_height
364 
365     @_debug "Camera resolution #{@video_width}x#{@video_height}px"

Also see: http://stackoverflow.com/questions/14256316/how-can-i-get-the-size-of-the-webcam-image-with-getusermedia

borisreitman commented 8 years ago

Although I can't reproduce the original problem, I can reproduced squished snapshot with this:

    Webcam.set({
      width: 666, // live preview size
      height: 500,
      dest_width: 666, // device capture size
      dest_height: 500,
      crop_width: 500, // final cropped size
      crop_height: 500,
      image_format: 'jpeg',
      jpeg_quality: 90
    });

screen shot 2015-12-15 at 1 41 33 pm

jhuckaby commented 8 years ago

Yeah, that is a known issue with some webcams. They can only display and capture video at 4:3 aspect ratio (or sometimes 16:9 aspect if you go above a certain size threshold). If you want a square or other oddly shaped image, it's best to keep the width, height, dest_width and dest_height at 4:3 or 16:9 aspect depending on the size, and then use crop_width and crop_height to achieve the final desired image you want.

So to use your previous example, if you want a 500x500 image, you should set your camera to capture at 888x500. This is a 16:9 aspect rectangle fitted onto your 500x500 box with bleed. Then the library will crop a 500x500 box from the center of that image. Example:

Webcam.set({
    width: 888, // live preview size
    height: 500,
    dest_width: 888, // device capture size
    dest_height: 500,
    crop_width: 500, // final cropped size
    crop_height: 500,
    image_format: 'jpeg',
    jpeg_quality: 90
});

Demo page: https://pixlcore.com/demos/webcamjs/demos/500x500.html

This works for me without any warping / distortion. But please keep in mind that many webcams are different. They all have little quirks and resolution or aspect ratio rules. It's very difficult to achieve a universal solution that works perfectly for all cameras on all browsers.

borisreitman commented 8 years ago

Using suggested parameters work fine in all browsers except Firefox. In firefox, the snapshot image is distorted. The snapshot image is not squished, but opposite: stretched.

borisreitman commented 8 years ago

As a temporary solution I will just use 3/4 ratio rectangle, without any crop. That works on Firefox too..

bencarter78 commented 7 years ago

I was also getting this issue on my MacBook Air but not on my iMac. The aspect ratio wasn't getting set properly, so when I captured an image it looked stretched.

Adding a 'constraint' seems to have fixed it for me.

options: {
  width: 720,
  height: 540,
  image_format: 'jpeg',
  jpeg_quality: 90
}

// Didn't work
WebCam.set(options)

// Working
WebCam.set('constraints', options)
neaumusic commented 7 years ago

You'd have to be an idiot to think all webcams are 4:3

jhuckaby commented 7 years ago

Let's keep it civil @neaumusic. Not everyone is as smart as you.

neaumusic commented 7 years ago

I honestly think you should delete this repo, or rather everything but the actual shockwave to video source implementation. I opened another issue where there's a transform being applied on top of the already scaled video.

jhuckaby commented 7 years ago

@neaumusic You've been reported to GitHub for abuse, and I am blocking you. I'm sorry this project didn't help you out. There are many webcam libraries out there. I hope you find what you are looking for.

killua-eu commented 6 years ago

@jhuckaby thanks lots for all the work you put into webcamjs, don't care about the trolls. is there anything we can do to help resolve this issue? our intended usage would greatly benefit from automatic aspect ratio detection

RenukaAnaparthi commented 6 years ago

@jhuckaby Thanks a lot for webcam.js. I used this in my application.but the problem is image got stretched after capturing for ratio other than 4:3.How can I resolve this?

davidercoli commented 4 years ago

I am a newbbie in Javascript, and I am currently working with webcamjs. Before some days of research in internet, I found this solution for the issue of aspect ratio. After the camera is loading and before taking the snapshot, I put the following code to correct the aspect ratio in the destination canvas:

        var videoTrack = Webcam.stream.getVideoTracks()[0];
        const settings = videoTrack.getSettings();
        Webcam.set('dest_width', Webcam.params.height * settings.aspectRatio);

I hope this can help you, best.

makc commented 4 years ago

I can reproduce this as well,

        Webcam.set ({
            width: W,
            height: H,
            enable_flash: false,
            image_format: 'jpeg',
            jpeg_quality: 90
        });

I have W > H; the lib creates the video with white padding on top and bottom, and when I snap it, inner contents of the video is scaled back to WxH without white bars.

jwaldimer commented 4 years ago

Thanks @davidercoli. I try with your solution and it worked for me!

xploshioOn commented 4 years ago

take in mind that the aspect ratio on iphone isn't available, but having the config of the camera you can set the dest with and height from camera itself so you will have the real values and after that with css you can show your image correctly depending on final resolution

const videoTrack = Webcam.stream.getVideoTracks()[0],
settings = videoTrack.getSettings();

// set values directly from camera
Webcam.set('dest_width', settings.width);
Webcam.set('dest_height', settings.height);
makc commented 4 years ago

@xploshioOn this kind of does not work. I mean, you do get the snapshot with the correct aspect ratio with this, but the bars are still there in the preview, and if you do this 2nd time after Webcam.reset() the preview video loses bars but gets stretched

Lassemannen commented 4 years ago

Hi there,

I was wondering if there is a way to avoid the stretching that occurs when using a mobile phone camera with webcamjs? It doesn't seem to matter which measurements I change in Webcam.set, the image still comes out stretched horizontally?

If you wonder what I mean, try using the webcamjs test page (https://pixlcore.com/demos/webcamjs/demos/500x500.html) with a mobile phone.

I'm really stumped here, grateful for any help.

xploshioOn commented 4 years ago

@makc we added that on our solution and the camera doesn't show any bars, and we change camera from back and front and there is no issue after resetting Webcam, maybe is another issue in your case?

baerenklau commented 4 years ago

Hi, we use a third party solution which uses WebcamJS and it works great with some standard webcams. We wanted to improve the picture quality and I started to use elgato Cam Link 4K with either a Sony and a Canon camera. As far as I see I am having more or less the same problem like borisreitman had. As the software is used by a lot of people with the same configuration I guess adding something like

Webcam.set({
    width: 888, // live preview size
    height: 500,
    dest_width: 888, // device capture size
    dest_height: 500,
    crop_width: 500, // final cropped size
    crop_height: 500,
    image_format: 'jpeg',
    jpeg_quality: 90
});

will not work for everyone but cause problems for the now working environments. If I use camera setting 3:2 there is a black bar left and right and the aspect ratio changes and with 16:9 there is no black bar but the aspect ratio changes as well. Do you see any chance for a general workaround that would not have a negative impact on the already correct working cameras? Thanks for any help.

Regards

Niko 16_9 3_2

edit: What puzzles me is the fact that the preview is fine but the taken image has the wrong aspect ratio. As far as I see the output is always 16:9 whatever I set in the camera because of the framegrabber wich takes HDMI (which is probably 16:9 per default) and outputs USB. With 16:9 the following kind of works:

        Webcam.set({
    width: 853, // live preview size
    height: 480,
    dest_width: 1067, // device capture size
    dest_height: 600,
    crop_width: 800, // final cropped size
    crop_height: 600,
    image_format: 'jpeg',
    jpeg_quality: 90
        });

while

        Webcam.set({
            width: 640,
            height: 480,
            dest_width: 800,
            dest_height: 600,
            image_format: 'jpeg',
            jpeg_quality: 90,
        });

does not. So probably the task is somehow guess if the camera delivers 16:9 and then crop the image. Happy to hear any comments.

renepardon commented 3 years ago

take in mind that the aspect ratio on iphone isn't available, but having the config of the camera you can set the dest with and height from camera itself so you will have the real values and after that with css you can show your image correctly depending on final resolution

const videoTrack = Webcam.stream.getVideoTracks()[0],
settings = videoTrack.getSettings();

// set values directly from camera
Webcam.set('dest_width', settings.width);
Webcam.set('dest_height', settings.height);

Error in mounted hook: "TypeError: Cannot read property 'getVideoTracks' of undefined"

joao-pratas-darabase commented 3 years ago

hey all. I just tried calling the drawImage without the width and height properties and seems to work.

so the drawImage call should be ctx.drawImage(canvas, 0, 0);

Seems to working for me, hope this helps.

Happy coding :)