Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Corrupted buffer when acess to the buffer is not fast enough (related to #450) #452

Open
1 task
jeffwitz opened this issue Apr 11, 2024 · 2 comments
Open
1 task

Comments

@jeffwitz
Copy link

jeffwitz commented Apr 11, 2024

Describe the Issue
I make a simple class that allows one to init a cam, open it, the get image from it and finally close it when needed. You can fond the code here :

To Reproduce
Steps to reproduce the behavior:
Launch this code with a camera that bugs.

Sample Code
I can show a piece of code that demonstrates the reported phenomenon:

  • [X ] Yes
  • No

You can change the :
sleep_bug if it is over 0.01, then it start to bug for me.

If yes, please provide a sample code:

from harvesters.core import Harvester
import numpy as np
import cv2
import time

class GenicamHarvesterCamera():
  def __init__(self):
    self.h = Harvester()
    self.h.add_file('/opt/mvIMPACT_Acquire/lib/x86_64/mvGenTLProducer.cti')
    self.h.update()
  def open(self):
    self.ia = self.h.create()
    self.ia.start()

  def get_image(self):
    with self.ia.fetch() as buffer:
      component = buffer.payload.components[0]
      width = component.width
      height = component.height
      num_comp = component.num_components_per_pixel
      data_format = component.data_format
      img = np.frombuffer(
        component.data, np.uint8).reshape((int(height), int(width)))
      if data_format == 'BayerRG8':
        img_data = cv2.cvtColor(img, cv2.COLOR_BAYER_BG2BGR)
      img_data = img_data.copy()
      return time.time(),img_data

  def close(self):
    self.ia.stop()
    self.ia.destroy()
    self.h.reset()

if __name__=='__main__':
  cam = GenicamHarvesterCamera()
  cam.open()
  cv2.namedWindow('frame', cv2.WINDOW_NORMAL)
  sleep_bug = 0.02
  while(True):
    # do something with the image
    time.sleep(sleep_bug)
    cv2.imshow('frame', cam.get_image()[1])
    if cv2.waitKey(1) & 0xFF == ord('q'):
        cam.close()
        cv2.destroyAllWindows()
        break

If applicable, please paste the actual output (its whole traceback, etc) here:
There is no errors, just corrupted buffers

Expected Behavior
no corrupted buffers, as everything is done with with
Configuration
Computer
Python 3.11.2 [GCC 12.2.0]
harvesters : 1.4.2
OS: Debian GNU/Linux 12 (bookworm) x86_64
Kernel: 6.6.13+bpo-rt-amd64
CPU: AMD Ryzen 7 PRO 7840U w/ Radeon 780M Graphics (16) @ 5.132GHz
GPU: AMD ATI c3:00.0 Phoenix1
GenTL Producer: /mvIMPACT_Acquire/lib/x86_64/mvGenTLProducer.cti
Camera

{'access_status': 1,
 'display_name': 'The Imaging Source Europe GmbH VID199E_PID9481_48910488', 
'id_': 'VID199E_PID9481_48910488', 'model': 'DFK 37AUX250',
 'parent': <genicam.gentl.Interface; proxy of <Swig Object of type 'std::shared_ptr< GenTLCpp::TLInterface > *' at 0x7fa203665800> >, 
'serial_number': '48910488', 'tl_type': 'U3V', 
'user_defined_name': 'DFK 37AUX250',
 'vendor': 'The Imaging Source Europe GmbH',
 'version': 'IMX250_C.MX/2396/1070 USB3mx-IMX-GS/6'
}

Reproducibility

This phenomenon can be stably reproduced:

  • [X ] Yes

If applicable, please provide your observation about the reproducibility.
Does not affect the other gige cam I have.

Actions You Have Taken
Explain it to you.

If it doesn't work, we will launch a thread that will implement is own buffer management in order work safely, it is not the thing we want to to, but a lot of time without success has passed ...

Additional context

@jeffwitz
Copy link
Author

Dear all,

I'm not sure you'll have time to deal with this problem in the Harvester and I still think it's a major problem for people who want to use Harvester to do image processing which can be a bit long between buffer acquisitions, I've made an example with a simplistic class that solves the problem, that you can find here :

import numpy as np
import time
from typing import Optional,Tuple, List, Dict, Any
import cv2
from harvesters.core import Harvester
from threading import Thread, RLock

class GenicamHarvesterCamera():
    def __init__(self) -> None:
        self.camera = None
        self.h = Harvester()
        self.h.add_file('/opt/mvIMPACT_Acquire/lib/x86_64/mvGenTLProducer.cti')
        self.h.update()
        self.num_image = 0
        self.channels = '1'
        self._frame_grabber = None
        self._lock = RLock()
        self._frame = None
        self._stop = False

    def open(self) -> None:
        self.ia = self.h.create()
        self.ia.start()
        self._frame_grabber = Thread(target=self._grab_frame)
        self._stop = False
        self._frame_grabber.start()

    def _grab_frame(self) -> None:
        while not self._stop:
            with self.ia.fetch() as buffer:
                component = buffer.payload.components[0]
                # Extraire les données nécessaires de component
                frame_data = {
                    'data': np.frombuffer(component.data, dtype=np.uint8).copy(),
                    'width': component.width,
                    'height': component.height,
                    'data_format': component.data_format
                }
            with self._lock:
                # Stocker les données extraites de manière thread-safe
                self._frame = frame_data

    def get_image(self) -> Tuple[Dict[str, Any], np.ndarray]:
        with self._lock:
            # Utiliser les données stockées pour créer l'image
            frame_data = self._frame
            img = frame_data['data'].reshape((frame_data['height'], frame_data['width']))
            img_data = self._process_image_data(img, frame_data['data_format'])

        metadata = {
            't(s)': time.time(),
            'ImageUniqueID': self.num_image,
        }
        self.num_image += 1
        return metadata, img_data

    def _process_image_data(self, img, data_format) -> np.ndarray:
        # Adapter cette méthode pour traiter les données d'image en fonction du format
        if data_format == 'BayerRG8':
            img_data = cv2.cvtColor(img, cv2.COLOR_BAYER_BG2BGR)
            if self.channels == '1':
                img_data = cv2.cvtColor(img_data, cv2.COLOR_BGR2GRAY)
            elif self.channels == '3':
                img_data = img_data.copy()
        return img_data

    def close(self):
        self._stop = True
        if self._frame_grabber is not None:
            self._frame_grabber.join()
        self.ia.stop()
        self.ia.destroy()
        self.h.reset()

if __name__=='__main__':
  cam = GenicamHarvesterCamera()
  cam.channels = '1'
  cam.open()
  cv2.namedWindow('frame', cv2.WINDOW_NORMAL)
  sleep_bug = 0.1
  while(True):
    # do something with the image
    time.sleep(sleep_bug) # you can change it with the Tread it never bugs anymore
    cv2.imshow('frame', cam.get_image()[1])
    if cv2.waitKey(1) & 0xFF == ord('q'):
        cam.close()
        cv2.destroyAllWindows()
        break

This remains a workaround that I find unsatisfactory, and for this reason I hope, if you agree, that the issue will remain open.

If someone finds this thread, then they'll have a solution until this problem is dealt with in the library itself.
Regards

@eli-osherovich
Copy link

@jeffwitz
Can you try to increase the number of buffers in the image acquirer?
Something like this:

ia = h.create()
ia.num_buffers=10

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants