[libcamera-devel] Software gathering of image statistics for libcamera on Librem 5
Pavel Machek
pavel at ucw.cz
Sat Dec 10 22:09:20 CET 2022
Hi!
> > Thanks for rkisp pointer. I'm quite confused by that code, not knowing
> > much about rkisp. Does it have some special support in hardware, like
> > providing one stream of frames for application and second for
> > statistics gathering?
>
> Yes. But when capturing RAW frames, it doesn't have the ability to
> provide statistics, so that means for RAW capture - we can't have a 3a
> loop running, and can only run with manual parameters.
>
>
> > For Librem 5 (and possibly PinePhone and similar), I'd like to snoop
> > on frames provided to the application, and set up exposure
> > accordingly. But I don't see any code in libcamera accessing image
> > data.
> >
> > Closest I could get is Image class (src/apps/common/image.h) used by
> > file sinks in cam application. Would it be acceptable to move that
> > into libcamera and use it to peek at the pixels?
>
> You can peek at pixels with the MappedFrameBuffer class.
>
> include/libcamera/internal/mapped_framebuffer.h
> src/libcamera/mapped_framebuffer.cpp
>
> If we only need 200 pixels to process, I'd be very happy to see a CPU
> based implementation that could be used on 'any' platform without an
> ISP.
Thank you. It looks like I can access all the neccessary information
from simple pipeline. I guess MappedPixels class should go somewhere
else; let me know if you have preferences for place and naming.
(There's more work to do, this just shows I have correct hooks).
Best regards,
Pavel
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
index a32de7f3..2980c636 100644
--- a/src/libcamera/pipeline/simple/simple.cpp
+++ b/src/libcamera/pipeline/simple/simple.cpp
@@ -27,6 +27,7 @@
#include <libcamera/control_ids.h>
#include <libcamera/request.h>
#include <libcamera/stream.h>
+#include <libcamera/formats.h>
#include "libcamera/internal/camera.h"
#include "libcamera/internal/camera_sensor.h"
@@ -35,6 +36,7 @@
#include "libcamera/internal/pipeline_handler.h"
#include "libcamera/internal/v4l2_subdevice.h"
#include "libcamera/internal/v4l2_videodevice.h"
+#include "libcamera/internal/mapped_framebuffer.h"
#include "converter.h"
@@ -213,6 +215,7 @@ public:
int setupFormats(V4L2SubdeviceFormat *format,
V4L2Subdevice::Whence whence);
void bufferReady(FrameBuffer *buffer);
+ void autoProcessing(Request *request);
unsigned int streamIndex(const Stream *stream) const
{
@@ -724,6 +727,135 @@ int SimpleCameraData::setupFormats(V4L2SubdeviceFormat *format,
return 0;
}
+class MappedPixels {
+public:
+ unsigned char *data;
+ PixelFormat format;
+ Size size;
+ int bpp;
+ int maxval;
+
+ MappedPixels(unsigned char *_data, const struct StreamConfiguration &_config) {
+ data = _data;
+ format = _config.pixelFormat;
+ size = _config.size;
+
+ switch (format) {
+ case formats::SGRBG8:
+ bpp = 1;
+ maxval = 255;
+ break;
+ case formats::SGRBG10:
+ bpp = 2;
+ maxval = 1023;
+ break;
+ default:
+ LOG(SimplePipeline, Error) << "Mapped pixels " << format << " is unknown format.";
+ }
+ }
+
+ int getValue(unsigned int x, unsigned int y) {
+ unsigned int v;
+ if (x >= size.width)
+ x = size.width - 1;
+ if (y >= size.height)
+ y = size.height - 1;
+ int i = (x + size.width * y) * bpp;
+ v = data[i];
+ if (bpp > 1)
+ v |= data[i+1] << 8;
+ return v;
+ }
+
+ int getMaxValue(unsigned int x, unsigned int y) {
+ int v, v2;
+
+ v = getValue(x, y);
+ v2 = getValue(x+1, y);
+ if (v2 > v)
+ v = v2;
+ v2 = getValue(x, y+1);
+ if (v2 > v)
+ v = v2;
+ v2 = getValue(x+1, y+1);
+ if (v2 > v)
+ v = v2;
+ return v;
+ }
+
+ float getMaxValueR(float x, float y) {
+ float v = getMaxValue(x * size.width, y * size.height);
+ return v/maxval;
+ }
+
+ void debugPaint(void) {
+ char map[] = " .,:;-+=*#";
+ for (float y = 0; y < 1; y += 1/25.) {
+ for (float x = 0; x < 1; x += 1/80.) {
+ float v = getMaxValueR(x, y);
+ printf("%c", map[ int (v * (sizeof(map) - 2)) ]);
+ }
+ printf("\n");
+ }
+ }
+};
+
+void SimpleCameraData::autoProcessing(Request *request)
+{
+ ControlList ctrls = sensor_->getControls({ V4L2_CID_EXPOSURE, V4L2_CID_ANALOGUE_GAIN });
+ static int exposure = 64;
+ static int gain = 64;
+
+ /* good place to add my processing ? */
+ for (auto [stream, buffer] : request->buffers()) {
+ MappedFrameBuffer mappedBuffer(buffer, MappedFrameBuffer::MapFlag::Read);
+ const std::vector<Span<uint8_t>> &planes = mappedBuffer.planes();
+ unsigned char *img = planes[0].data();
+ const struct StreamConfiguration &config = stream->configuration();
+ MappedPixels pixels(img, config);
+
+ LOG(SimplePipeline, Error) << config.pixelFormat << " " << config.size;
+
+ pixels.debugPaint();
+
+ int bright = 0, too_bright = 0, total = 0;
+ int adjust = 0;
+
+ for (float y = 0; y < 1; y += 1/30.) {
+ for (float x = 0; x < 1; x += 1/40.) {
+ float v = pixels.getMaxValueR(x, y);
+
+ total++;
+ if (v > 240./255)
+ too_bright++;
+ if (v > 200./255)
+ bright++;
+ }
+ }
+
+ if ((bright / (float) total) < 0.01)
+ adjust = +1;
+ if ((too_bright / (float) total) > 0.08)
+ adjust = -1;
+
+ exposure += adjust;
+ gain += adjust;
+ printf("image data -- %d %d -- %d -- exp %d\n",
+ (int) img[0], (int) img[1], adjust, exposure);
+
+ ctrls.set(V4L2_CID_EXPOSURE, exposure);
+ ctrls.set(V4L2_CID_ANALOGUE_GAIN, gain);
+ }
+#if 0
+ const ControlInfoMap &infoMap = controls();
+
+ if (infoMap.find(V4L2_CID_BRIGHTNESS) != infoMap.end()) {
+ //const ControlInfo &brightness = infoMap.find(V4L2_CID_BRIGHTNESS)->second;
+ }
+#endif
+
+}
+
void SimpleCameraData::bufferReady(FrameBuffer *buffer)
{
SimplePipelineHandler *pipe = SimpleCameraData::pipe();
@@ -739,6 +871,7 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
Request *request = buffer->request();
pipe->completeBuffer(request, buffer);
pipe->completeRequest(request);
+
return;
}
@@ -761,8 +894,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
}
converterQueue_.pop();
- if (request)
+ if (request) {
pipe->completeRequest(request);
+ }
return;
}
@@ -808,6 +942,7 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
/* Otherwise simply complete the request. */
pipe->completeBuffer(request, buffer);
+ autoProcessing(request);
pipe->completeRequest(request);
}
@@ -823,8 +958,10 @@ void SimpleCameraData::converterOutputDone(FrameBuffer *buffer)
/* Complete the buffer and the request. */
Request *request = buffer->request();
- if (pipe->completeBuffer(request, buffer))
+ if (pipe->completeBuffer(request, buffer)) {
+ autoProcessing(request);
pipe->completeRequest(request);
+ }
}
/* Retrieve all source pads connected to a sink pad through active routes. */
--
People of Russia, stop Putin before his war on Ukraine escalates.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 195 bytes
Desc: not available
URL: <https://lists.libcamera.org/pipermail/libcamera-devel/attachments/20221210/6a49f8a0/attachment.sig>
More information about the libcamera-devel
mailing list