The objective is to use OpenCV 3 in C. OpenCV had a C API, but it has been deprecated since a long time ago.
So what I did is an abstraction in C++ converting all pointers to class cv::Something
into void *
which I can't dereference in C, but can pass between C++ extern "C"
functions which do the work.
To use this abstraction, I did some C function, which should read an image from a file and save it into a new file:
#include "foo.h"
#include "libalx/extra/cv.h"
int foo(const char *restrict src, const char *restrict dest)
{
void *img;
int status;
status = -1;
if (alx_cv_alloc_img(&img))
return -1;
if (alx_cv_init_img(img, 1, 1)) // Segfault is here
goto out_free;
if (alx_cv_imread(img, src))
goto out_free;
if (alx_cv_imwrite(img, dest))
goto out_free;
status = 0;
out_free:
alx_cv_free_img(img);
return status;
}
The files needed to create this abstraction are the following:
cv.h
:
#pragma once /* libalx/extra/cv.h */
#include <stddef.h>
__attribute__((nonnull))
int alx_cv_alloc_img(void **restrict img);
__attribute__((nonnull))
void alx_cv_free_img (void *restrict img);
__attribute__((nonnull))
int alx_cv_init_img (void *restrict img, ptrdiff_t w, ptrdiff_t h);
__attribute__((nonnull))
int alx_cv_imread (void *restrict img, const char *restrict fname);
__attribute__((nonnull))
void alx_cv_imwrite (const void *restrict img, const char *restrict fname);
cv.hpp
:
#pragma once /* libalx/extra/cv.hpp */
#include <cstddef>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#define restrict __restrict__
extern "C"
{
[[gnu::nonnull]]
int alx_cv_alloc_img(void **restrict img);
[[gnu::nonnull]]
void alx_cv_free_img (void *restrict img);
[[gnu::nonnull]]
int alx_cv_init_img (void *restrict img, ptrdiff_t w, ptrdiff_t h);
[[gnu::nonnull]]
int alx_cv_imread (void *restrict img, const char *restrict fname);
[[gnu::nonnull]]
void alx_cv_imwrite (const void *restrict img, const char *restrict fname);
} /* extern "C" */
namespace alx {
namespace CV {
[[gnu::nonnull]]
int alloc_img (class cv::Mat **restrict img);
[[gnu::nonnull]]
void free_img (class cv::Mat *restrict img);
[[gnu::nonnull]]
int init_img (class cv::Mat *restrict rect,
ptrdiff_t w, ptrdiff_t h);
[[gnu::nonnull]]
int imread (class cv::Mat *restrict img,
const char *restrict fname);
[[gnu::nonnull]]
void imwrite (const class cv::Mat *restrict img,
const char *restrict fname);
} /* namespace CV */
} /* namespace alx */
cv.cpp
:
#include "libalx/extra/cv.hpp"
#include <cstddef>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "libalx/base/stdlib/alloc/mallocs.hpp"
#define restrict __restrict__
int alx::CV::alloc_img (class cv::Mat **restrict img)
{
return alx_mallocs(img, 1);
}
int alx_cv_alloc_img (void **restrict img)
{
return alx::CV::alloc_img((class cv::Mat **)img);
}
void alx::CV::free_img (class cv::Mat *restrict img)
{
img->release();
free(img);
}
void alx_cv_free_img (void *restrict img)
{
alx::CV::free_img((class cv::Mat *)img);
}
int alx::CV::init_img (class cv::Mat *restrict img,
ptrdiff_t w, ptrdiff_t h)
{
if (w < 1 || h < 1)
return 1;
*img = cv::Mat::zeros(cv::Size(w, h), CV_8UC3); // Segfault is here
return 0;
}
int alx_cv_init_img (void *restrict img, ptrdiff_t w, ptrdiff_t h)
{
return alx::CV::init_img((class cv::Mat *)img, w, h);
}
int alx::CV::imread (class cv::Mat *restrict img,
const char *restrict fname)
{
*img = cv::imread(fname, CV_LOAD_IMAGE_COLOR);
if (img->empty())
return -1;
return 0;
}
int alx_cv_imread (void *restrict img, const char *restrict fname)
{
return alx::CV::imread((class cv::Mat *)img, fname);
}
void alx::CV::imwrite (const class cv::Mat *restrict img,
const char *restrict fname)
{
cv::imwrite(fname, *img);
}
void alx_cv_imwrite (const void *restrict img,
const char *restrict fname)
{
alx::CV::imwrite((const class cv::Mat *)img, fname);
}
Here I use alx_mallocs
which is an enclosure over malloc
:
mallocs.hpp
:
#pragma once /* libalx/base/stdlib/alloc/mallocs.hpp */
#include <cstddef>
/*
* [[gnu::nonnull]]
* int alx_mallocs(type **restrict p, ptrdiff_t nmemb);
*/
#define alx_mallocs(ptr, nmemb) ( \
{ \
auto ptr_ = (ptr); \
void *vp; \
\
vp = alx_mallocs__(nmemb, sizeof(**ptr_)); \
*ptr_ = static_cast<typeof(*ptr_)>(vp); \
\
!(*ptr_); \
} \
)
extern "C"
{
[[gnu::malloc]]
void *alx_mallocs__(ptrdiff_t nmemb, size_t size);
}
mallocs.c
:
#include "libalx/base/stdlib/alloc/mallocs.h"
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
void *alx_mallocs__(ptrdiff_t nmemb, size_t size)
{
if (nmemb < 0)
goto ovf;
if (nmemb > (PTRDIFF_MAX / (ptrdiff_t)size))
goto ovf;
return malloc(size * nmemb);
ovf:
errno = EOVERFLOW;
return NULL;
}
However, it seems that allocating and trying to initialize the allocated memory isn't enough. My guess is that there are some constructors that aren't being called.
How can this be done so that it doesn't segfault?
instead of trying to call the copy assignment operator with this
*img = cv::Mat::zeros(cv::Size(w, h), CV_8UC3);
You can use placement-new to construct an object into already allocated memory
new (img) cv::Mat(cv::Size(w, h), CV_8UC3, 0.0);
However you should also make sure that you are honoring the alignment of a type when constructing it into previously allocated memory.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With