OpenCV Mat to array access - c++

I have problem with access to data from Mat.data. I perform operations on picture and I need access to every pixel separately.
I have to necessairly operate on simple types (float, int etc).
The way I am accesing data is like below:
for (int idx = 0; idx < image.rows; idx++) {
for (int idy = 0; idy < image.cols; idy++) {
int color_tid = idx * image.cols * image.channels() + idy * image.channels();
uint8_t blue = image.data[color_tid];
uint8_t green = image.data[color_tid + 1];
uint8_t red = image.data[color_tid + 2];
float pixelVal = (int) blue + (int) green + (int) red;
(...)
}
}
This approach is working correctly only to square images (NxN pixels), but for NxM there are anomalies outside the square area (smaller edge).
Do anyone know any other way to acces data of the picture Mat?
Example image (correct result):
anomalies (my problem)

I recommend to follow the data layout in a Mat
so your loop becomes:
for (int r = 0; r < img.rows; ++r)
{
for (int c = 0; c < img.cols; ++c)
{
uchar* ptr = img.data + img.step[0] * r + img.step[1] * c;
uchar blue = ptr[0];
uchar green = ptr[1];
uchar red = ptr[2];
float pixelVal = blue + green + red;
}
}
You can eventually perform a little less operations like:
for (int r = 0; r < img.rows; ++r)
{
uchar* pt = img.data + img.step[0] * r;
for (int c = 0; c < img.cols; ++c)
{
uchar* ptr = pt + img.step[1] * c;
uchar blue = ptr[0];
uchar green = ptr[1];
uchar red = ptr[2];
float pixelVal = blue + green + red;
}
}

The code in your question contains a few flaws:
rows and columns are swapped (row is Y, column is X)
step size between rows (aka "stride") does not always equal to the number of columns
Using Mat::at<> makes the code much simpler:
for(int row = 0; row < image.rows; ++row)
{
for(int col = 0; col < image.cols; ++col)
{
const Vec3b& pt = image.at<Vec3b>(row, col);
float pixelVal = pt[0] + pt[1] + pt[2];
...
}
}

Related

Scale image in C++ with OpenCV but not using pyrDown() nor pyrUp() functions

Hello I'm trying to scale x2 an image to decrease or increase it, but I am not allowed to call the pyr functions in the code. Here's what I have so far for decreasing the image:
cv::Mat Pyramid::calcPyramid(cv::Mat image)
{
int channels = image.channels();
int rows = image.rows;
int cols = image.cols*image.channels();
cv::Mat calculada(image.rows/2, image.cols/2, CV_8UC3);
uchar* prixelPtr = (uchar*)image.data;
for(int iRows = 0; iRows < rows; iRows++)
{
for(int iCols=0; iCols < cols; iCols++)
{
uchar blue = prixelPtr[ 2*iRows*image.cols*channels + 2*iCols*channels + 0];
uchar green = prixelPtr[ 2*iRows*image.cols*channels + 2*iCols*channels + 1];
uchar red = prixelPtr[ 2*iRows*image.cols*channels + 2*iCols*channels + 2];
calculada.at<cv::Vec3b>(iRows, iCols)[0] = blue;
calculada.at<cv::Vec3b>(iRows, iCols)[1] = green;
calculada.at<cv::Vec3b>(iRows, iCols)[2] = red;
}
}
return calculada;
}
The error I'm getting on the terminal is: Segmentation fault (core dumped). How can I fix the code?
Thanks!
The code is simple:
src and dst must be separate images, not the same matrix.
void pyrDown(cv::Mat& src, cv::Mat& dst)
{
dst = cv::Mat(src.rows / 2, src.cols / 2, CV_8UC3);
for (int i = 0; i < src.rows/2; ++i)
{
for (int j = 0; j < src.cols/2; ++ j)
{
// sample 4 points from source image
cv::Vec3f p11 = src.at<cv::Vec3b>(i * 2, j * 2);
cv::Vec3f p12 = src.at<cv::Vec3b>(i * 2, j * 2+1);
cv::Vec3f p21 = src.at<cv::Vec3b>(i * 2+1, j * 2);
cv::Vec3f p22 = src.at<cv::Vec3b>(i * 2+1, j * 2+1);
// find mean (we want to keep as much information as possible)
cv::Vec3f p_dst = (p11 + p12 + p21 + p22) / 4.0f;
// put result to destination image
dst.at<cv::Vec3b>(i, j) = p_dst;
}
}
}
But it would be great to add boundary checking conditions, hope you'll manage it :)

How to set OpenCV Mat as Tensorflow Lite input and output?

I'm trying to use GPU Delegate in Tensorflow Lite on iOS. My model has inputs and outputs as OpenCV BGR image ([258, 540, 3]). How can I set inputs and outputs in C++ tensorflow lite interpreter? I tried to use this code
int input = interpreter->inputs()[0];
float* out = interpreter->typed_tensor<float>(input);
NSData* slicedData = [self inputDataFromCvMat:slicedImage];
uint8_t* in = (uint8_t*) slicedData.bytes;
ProcessInputWithFloatModel(in, out, WIDTH, HEIGHT, CHANNELS);
void ProcessInputWithFloatModel(uint8_t* input, float* buffer, int image_width, int image_height, int image_channels) {
for (int y = 0; y < wanted_input_height; ++y) {
float* out_row = buffer + (y * wanted_input_width * wanted_input_channels);
for (int x = 0; x < wanted_input_width; ++x) {
const int in_x = (y * image_width) / wanted_input_width;
const int in_y = (x * image_height) / wanted_input_height;
uint8_t* input_pixel =
input + (in_y * image_width * image_channels) + (in_x * image_channels);
float* out_pixel = out_row + (x * wanted_input_channels);
for (int c = 0; c < wanted_input_channels; ++c) {
out_pixel[c] = (input_pixel[c] - input_mean) / input_std;
}
}
}
}
- (NSData *)inputDataFromCvMat:(Mat)image {
NSMutableData *inputData = [[NSMutableData alloc] initWithCapacity:0];
for (int row = 0; row < HEIGHT + 10; row++) {
for (int col = 0; col < WIDTH + 10; col++) {
Vec3b intensity = image.at<Vec3b>(row, col);
int blue = intensity.val[0];
int green = intensity.val[1];
int red = intensity.val[2];
// we need to put pixel values in BGR (model was trained with opencv)
[inputData appendBytes:&blue length:sizeof(blue)];
[inputData appendBytes:&green length:sizeof(green)];
[inputData appendBytes:&red length:sizeof(red)];
}
}
return inputData;
}
but I don't know what is wrong
After some research, I managed to get it working
const int wanted_input_width = 258;
const int wanted_input_height = 540;
const int wanted_input_channels = 3;
Mat image = ...
// write to input
int input = interpreter->inputs()[0];
float* out = interpreter->typed_tensor<float>(input);
uint8_t* in = image.ptr<uint8_t>(0);
ProcessInputWithFloatModel(in, out);
// run interpreter
if (interpreter->Invoke() != kTfLiteOk) {
LOG(FATAL) << "Failed to invoke!";
}
// get output
int output_idx = interpreter->outputs()[0];
float* output = interpreter->typed_output_tensor<float>(output_idx);
Mat outputMat = ProcessOutputWithFloatModel(output);
/// Preprocess the input image and feed the TFLite interpreter buffer for a float model.
void ProcessInputWithFloatModel(uint8_t* input, float* buffer) {
for (int y = 0; y < wanted_input_height; ++y) {
float* out_row = buffer + (y * wanted_input_width * wanted_input_channels);
for (int x = 0; x < wanted_input_width; ++x) {
uint8_t* input_pixel = input + (y * wanted_input_width * wanted_input_channels) + (x * wanted_input_channels);
float* out_pixel = out_row + (x * wanted_input_channels);
for (int c = 0; c < wanted_input_channels; ++c) {
out_pixel[c] = input_pixel[c] / 255.0f;
}
}
}
}
Mat ProcessOutputWithFloatModel(float* input) {
cv::Mat image = cv::Mat::zeros(wanted_input_height, wanted_input_width, CV_8UC3);
for (int y = 0; y < wanted_input_height; ++y) {
for (int x = 0; x < wanted_input_width; ++x) {
float* input_pixel = input + (y * wanted_input_width * wanted_input_channels) + (x * wanted_input_channels);
cv::Vec3b & color = image.at<cv::Vec3b>(cv::Point(x, y));
color[0] = (uchar) floor(input_pixel[0] * 255.0f);
color[1] = (uchar) floor(input_pixel[1] * 255.0f);
color[2] = (uchar) floor(input_pixel[2] * 255.0f);
}
}
return image;
}

opencv::Mat, get pixel value from the raw data?

I have an image, as a cv::Mat. I am getting the raw data from this, with:
uchar* data = (uchar *)pImg.data;
I need to pass this data to a function, then cycle through each pixel of the image. I would have done:
for (int i = 0; i < image.rows; ++i)
{
for (int j = 0; j < image.cols; ++j)
{
//pixel = cv::Point(i,j);
}
}
What is the equivalent of this, using the uchar* data?
It is pretty easy but you need to remember one thing, this image.elemSize() indicates how many bytes there are per pixel (this function is taken from OpenCV mat). So this loop will look little bit different for different image formats. There is a example inside the loop
for (auto i = 0; i < image.rows * image.cols; i+=image.elemSize())
{
//for CV_8UC1
//auto pixel = *(image.data + i)
//for RGB as CV_8UC3
auto r = *(image.data + i)
auto g = *(image.data + i + 1)
auto b = *(image.data + i + 2)
}
The correct pixel value can be accessed from the raw data provided the following parameters are known:
X coordinate of pixel ( column number )
Y coordinate of pixel ( row number )
Image depth (actual data type of a single pixel i.e. uchar, ushort, float etc)
Number of channels of the image
Image step in bytes
Given the above information, the pixel can be accessed as follows (for CV_8UC3 type):
uchar* data = (uchar *)pImg.data;
for (int i = 0; i < image.rows; ++i)
{
for (int j = 0; j < image.cols; ++j)
{
uchar b = data[i * pImg.step + pImg.channels() * j + 0];
uchar g = data[i * pImg.step + pImg.channels() * j + 1];
uchar r = data[i * pImg.step + pImg.channels() * j + 2];
}
}

Copy one channel data to another channel in Opencv

how can I copy red channel value of a mat image to blue channel using opencv.
Thanks in advance.
cv::Mat Image =cv::imread("image.jpg");
uint8_t * orig_ptr = (uint8_t*)Image.data;
for (int y = 0; y < Image.rows; y++)
{
for (int x = 0; x < Image.cols; x++)
{
int R = orig_ptr[x * 3 + y*Image.step + 2];
orig_ptr[x * 3 + y*Image.step + 1] = R;
orig_ptr[x * 3 + y*Image.step] = R;
}
}

OpenCV get pixel channel value from Mat image

Maybe I'm not looking hard enough, but everything seems to want me to use an array. Thus, how do I get the channel value for a particular pixel for foo if foo is something like Mat foo = imread("bar.png")?
Assuming the type is CV_8UC3 you would do this:
for(int i = 0; i < foo.rows; i++)
{
for(int j = 0; j < foo.cols; j++)
{
Vec3b bgrPixel = foo.at<Vec3b>(i, j);
// do something with BGR values...
}
}
Here is the documentation for Vec3b. Also, don't forget OpenCV stores things internally as BGR not RGB.
EDIT :
For performance reasons, you may want to use direct access to the data buffer in order to process the pixel values:
Here is how you might go about this:
uint8_t* pixelPtr = (uint8_t*)foo.data;
int cn = foo.channels();
Scalar_<uint8_t> bgrPixel;
for(int i = 0; i < foo.rows; i++)
{
for(int j = 0; j < foo.cols; j++)
{
bgrPixel.val[0] = pixelPtr[i*foo.cols*cn + j*cn + 0]; // B
bgrPixel.val[1] = pixelPtr[i*foo.cols*cn + j*cn + 1]; // G
bgrPixel.val[2] = pixelPtr[i*foo.cols*cn + j*cn + 2]; // R
// do something with BGR values...
}
}
Or alternatively:
int cn = foo.channels();
Scalar_<uint8_t> bgrPixel;
for(int i = 0; i < foo.rows; i++)
{
uint8_t* rowPtr = foo.row(i);
for(int j = 0; j < foo.cols; j++)
{
bgrPixel.val[0] = rowPtr[j*cn + 0]; // B
bgrPixel.val[1] = rowPtr[j*cn + 1]; // G
bgrPixel.val[2] = rowPtr[j*cn + 2]; // R
// do something with BGR values...
}
}
The below code works for me, for both accessing and changing a pixel value.
For accessing pixel's channel value :
for (int i = 0; i < image.cols; i++) {
for (int j = 0; j < image.rows; j++) {
Vec3b intensity = image.at<Vec3b>(j, i);
for(int k = 0; k < image.channels(); k++) {
uchar col = intensity.val[k];
}
}
}
For changing a pixel value of a channel :
uchar pixValue;
for (int i = 0; i < image.cols; i++) {
for (int j = 0; j < image.rows; j++) {
Vec3b &intensity = image.at<Vec3b>(j, i);
for(int k = 0; k < image.channels(); k++) {
// calculate pixValue
intensity.val[k] = pixValue;
}
}
}
`
Source : Accessing pixel value
The pixels array is stored in the "data" attribute of cv::Mat. Let's suppose that we have a Mat matrix where each pixel has 3 bytes (CV_8UC3).
For this example, let's draw a RED pixel at position 100x50.
Mat foo;
int x=100, y=50;
Solution 1:
Create a macro function that obtains the pixel from the array.
#define PIXEL(frame, W, x, y) (frame+(y)*3*(W)+(x)*3)
//...
unsigned char * p = PIXEL(foo.data, foo.rols, x, y);
p[0] = 0; // B
p[1] = 0; // G
p[2] = 255; // R
Solution 2:
Get's the pixel using the method ptr.
unsigned char * p = foo.ptr(y, x); // Y first, X after
p[0] = 0; // B
p[1] = 0; // G
p[2] = 255; // R