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;
}
Related
We would like to perform bokeh blur on a image. I have tried to test some code below but could not get Circle of Confusion on bright point.
void bokeh(unsigned char *Input, unsigned char *Output, int Width, int Height, int Stride, int Radius)
{
int Channels = Stride / Width;
int rsq = fmax(1, sqrtf(Radius));
for (int y = 0; y < Height; y++)
{
unsigned char * LinePD = Output + y*Stride;
for (int x = 0; x < Width; x++)
{
unsigned int sum[3] = { 0 };
unsigned int weightsum = 0;
for (int ny = std::max(0, y - Radius); ny < std::min(y + Radius, Height); ny++)
{
const unsigned char * sampleLine = Input + ny*Stride;
for (int nx = std::max(0, x - Radius); nx < std::min(x + Radius, Width); nx++)
{
if (sqrtf(nx - x) + sqrtf(ny - y) < rsq)
{
const unsigned char * sample = sampleLine + nx*Channels;
const unsigned char&R = sample[0];
const unsigned char&G = sample[1];
const unsigned char&B = sample[2];
float weight = sqrtf((unsigned char)((21627 * R + 21627 * G + 21627 * B) >> 16));
for (int c = 0; c < Channels; c++)
{
sum[c] += weight*sample[c];
}
weightsum += weight;
}
}
}
for (int c = 0; c < Channels; c++)
{
LinePD[c] = ClampToByte(sum[c] / weightsum);
}
LinePD += Channels;
}
}
}
The source image is:
The result is:
while I expect effect is which like circular in pictures below
seems that I replace sqrtf(nx - x) + sqrtf(ny - y) < rsq
with
powf(nx - x, 2.0) + powf(ny - y, 2.0) < powf(Radius, 2)
and replace float weight = sqrtf((unsigned char)((21627 * R + 21627 * G + 21627 * B) >> 16));
with
float weight = (R + G + B)*1.0f/3.0f;
I could get bokeh blur effect, so how to set the weight to by brightness?
I am trying to adjust the brightness and contrast of an RGB image but the output is not what I expect.
The function is callbacked from createTrackbar() function with values from 0 to 100.
Please check the image below. I would appreciate some help. Thanks.
void brightness_callback(int brightness, void *userdata)
{
int height = image_input.rows, width = image_input.cols;
image_output = Mat::zeros(image_input.size(), image_input.type());
int widthStep = image_input.step;
int nChannels = 3;
uchar *pDataInput = (uchar *)image_input.data;
uchar *pDataOutput = (uchar *)image_output.data;
for (int x = 0; x < height; x++, pDataInput += widthStep, pDataOutput += widthStep) {
uchar *pRowInput = pDataInput;
uchar *pRowOutput = pDataOutput;
for (int y = 0; y < width; y++, pRowInput += nChannels, pRowOutput += nChannels) {
uchar B = pRowInput[0];
uchar G = pRowInput[1];
uchar R = pRowInput[2];
pRowOutput[0] = truncate((uchar)(B + brightness));
pRowOutput[1] = truncate((uchar)(G + brightness));
pRowOutput[2] = truncate((uchar)(R + brightness));
}
}
imshow(window_original, image_output);
}
uchar truncate(uchar value) {
if (value < 0) return 0;
else if (value > 255) return 255;
return value;
}
I want to find dominant N colors on the picture. For this purpose I decided to use KMeans algorithm. My project written on C, that is way I used cvKMeans2 algorithm. But it gives me very strange results. Then I decided to try kmeans algorithm on OpenCV C++. It gives me more accurate results. So, where is my fault? Could someone explain it to me?
1. I used this image for test.
2. Implementation on C.
#include <cv.h>
#include <highgui.h>
#define CLUSTERS 3
int main(int argc, char **argv) {
const char *filename = "test_12.jpg";
IplImage *tmp = cvLoadImage(filename);
if (!tmp) {
return -1;
}
IplImage *src = cvCloneImage(tmp);
cvCvtColor(tmp, src, CV_BGR2RGB);
CvMat *samples = cvCreateMat(src->height * src->width, 3, CV_32F);
for (int i = 0; i < samples->height; i++) {
samples->data.fl[i * 3 + 0] = (uchar) src->imageData[i * 3 + 0];
samples->data.fl[i * 3 + 1] = (uchar) src->imageData[i * 3 + 1];
samples->data.fl[i * 3 + 2] = (uchar) src->imageData[i * 3 + 2];
}
CvMat *labels = cvCreateMat(samples->height, 1, CV_32SC1);
CvMat *centers = cvCreateMat(CLUSTERS, 3, CV_32FC1);
int flags = 0;
int attempts = 5;
cvKMeans2(samples, CLUSTERS, labels,
cvTermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 10000, 0.005),
attempts, 0, flags, centers);
int rows = 40;
int cols = 300;
IplImage *des = cvCreateImage(cvSize(cols, rows), 8, 3);
int part = 4000;
int r = 0;
int u = 0;
for (int y = 0; y < 300; ++y) {
for (int x = 0; x < 40; ++x) {
if (u >= part) {
r++;
part = (r + 1) * part;
}
des->imageData[(300 * x + y) * 3 + 0] = static_cast<char>(centers->data.fl[r * 3 + 0]);
des->imageData[(300 * x + y) * 3 + 1] = static_cast<char>(centers->data.fl[r * 3 + 1]);
des->imageData[(300 * x + y) * 3 + 2] = static_cast<char>(centers->data.fl[r * 3 + 2]);
u++;
}
}
IplImage *dominant_colors = cvCloneImage(des);
cvCvtColor(des, dominant_colors, CV_BGR2RGB);
cvNamedWindow("dominant_colors", CV_WINDOW_AUTOSIZE);
cvShowImage("dominant_colors", dominant_colors);
cvWaitKey(0);
cvDestroyWindow("dominant_colors");
cvReleaseImage(&src);
cvReleaseImage(&des);
cvReleaseMat(&labels);
cvReleaseMat(&samples);
return 0;
}
3. Implementation on C++.
#include <cv.h>
#include <opencv/cv.hpp>
#define CLUSTERS 3
int main(int argc, char **argv) {
const cv::Mat &tmp = cv::imread("test_12.jpg");
cv::Mat src;
cv::cvtColor(tmp, src, CV_BGR2RGB);
cv::Mat samples(src.rows * src.cols, 3, CV_32F);
for (int y = 0; y < src.rows; y++)
for (int x = 0; x < src.cols; x++)
for (int z = 0; z < 3; z++)
samples.at<float>(y + x * src.rows, z) = src.at<cv::Vec3b>(y, x)[z];
int attempts = 5;
cv::Mat labels;
cv::Mat centers;
kmeans(samples, CLUSTERS, labels, cv::TermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 1000, 0.005),
attempts, cv::KMEANS_PP_CENTERS, centers);
cv::Mat colors(cv::Size(CLUSTERS * 100, 30), tmp.type());
int p = 100;
int cluster_id = 0;
for (int x = 0; x < CLUSTERS * 100; x++) {
for (int y = 0; y < 30; y++) {
if (x >= p) {
cluster_id++;
p = (cluster_id + 1) * 100;
}
colors.at<cv::Vec3b>(y, x)[0] = static_cast<uchar>(centers.at<float>(cluster_id, 0));
colors.at<cv::Vec3b>(y, x)[1] = static_cast<uchar>(centers.at<float>(cluster_id, 1));
colors.at<cv::Vec3b>(y, x)[2] = static_cast<uchar>(centers.at<float>(cluster_id, 2));
}
}
cv::Mat dominant_colors;
cv::cvtColor(colors, dominant_colors, CV_RGB2BGR);
cv::imshow("dominant_colors", dominant_colors);
cv::waitKey(0);
return 0;
}
4. Result of code on C.
5. Result of code on C++.
I found my mistake. It is related to IplImage's widthStep field. As I read here widthStep gets padded up to a multiple of 4 for performance reasons. If widthStep is equal to 30 it will padded up to 32.
int h = src->height;
int w = src->width;
int c = 3;
int delta = 0;
for (int i = 0, y = 0; i < h; ++i) {
for (int j = 0; j < w; ++j) {
for (int k = 0; k < c; ++k, y++) {
samples->data.fl[i * w * c + c * j + k] = (uchar) src->imageData[delta + i * w * c + c * j + k];
}
}
delta += src->widthStep - src->width * src->nChannels;
}
With pointers
for (int x = 0, i = 0; x < src->height; ++x) {
auto *ptr = (uchar *) (src->imageData + x * src->widthStep);
for (int y = 0; y < src->width; ++y, i++) {
for (int j = 0; j < 3; ++j) {
samples->data.fl[i * 3 + j] = ptr[3 * y + j];
}
}
}
The TMX map is loading correctly but it seems to be positioning my tiles incorrectly.
I'm using the TMX Parser from here: https://code.google.com/p/tmx-parser/
It loads the TMX fine, with no errors. But it's only positioning the tiles according to the their location in the spritesheet.
Here is the code sample:
void Game::DrawMap()
{
SDL_Rect rect_CurTile;
SDL_Rect pos;
int DrawX;
int DrawY;
for (int i = 0; i < map->GetNumLayers(); ++i)
{
// Get a layer.
currLayer = map->GetLayer(i);
for (int x = 0; x < currLayer->GetWidth(); ++x)
{
for (int y = 0; y < currLayer->GetHeight(); ++y)
{
int CurTile = currLayer->GetTileId(x, y);
int Num_Of_Cols = 8;
int tileset_col = (CurTile % Num_Of_Cols);
tileset_col++;
int tileset_row = (CurTile / Num_Of_Cols);
rect_CurTile.x = (1 + (32 + 1) * tileset_col);
rect_CurTile.y = (1 + (32 + 1) * tileset_row);
rect_CurTile.w = 32;
rect_CurTile.h = 32;
DrawX = (x * 32);
DrawY = (y * 32);
pos.x = DrawX;
pos.y = DrawY;
pos.w = 32;
pos.h = 32;
apply_surfaceClip(DrawX,DrawY, surfaceTileset, destSurface, &rect_CurTile);
sprTexture = SDL_CreateTextureFromSurface(mRenderer,destSurface);
SDL_RenderCopy(mRenderer,sprTexture,&rect_CurTile,&pos);
}
}
}
void apply_surfaceClip( int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip = NULL )
{
//Holds offsets
SDL_Rect offset;
//Get offsets
offset.x = x;
offset.y = y;
//Blit
SDL_BlitSurface( source, clip, destination, &offset );
}
I fixed the issue the problem was when using two layers it was drawing zeros here is the finished sample
for (int i = 0; i < map->GetNumLayers(); ++i)
{
// Get a layer.
currLayer = map->GetLayer(i);
for (int x = 0; x < currLayer->GetWidth(); ++x)
{
for (int y = 0; y < currLayer->GetHeight(); ++y)
{
int CurTile = currLayer->GetTileId(x, y);
if(CurTile == 0)
{
continue;
}
int Num_Of_Cols = 8;
int tileset_col = (CurTile % Num_Of_Cols);
int tileset_row = (CurTile / Num_Of_Cols);
std::cout << CurTile << std::endl;
rect_CurTile.x = (1 + (32 + 1) * tileset_col);
rect_CurTile.y = (1 + (32 + 1) * tileset_row);
rect_CurTile.w = 32;
rect_CurTile.h = 32;
DrawX = (x * 32);
DrawY = (y * 32);
pos.x = DrawX;
pos.y = DrawY;
pos.w = 32;
pos.h = 32;
apply_surfaceClip(DrawX,DrawY, surfaceTileset, destSurface, &rect_CurTile);
sprTexture = SDL_CreateTextureFromSurface(mRenderer,destSurface);
SDL_RenderCopy(mRenderer,sprTexture,&rect_CurTile,&pos);
}
}
}
i want to transport the follow codes into c++:
gaussFilter = fspecial('gaussian', 2*neighSize+1, 0.5*neighSize);
pointFeature = imfilter(pointFeature, gaussFilter, 'symmetric');
where the pointFeature is a [height, width, 24] array.
i try to use filter2D, but it only support the 2D array.
so i want to know if there are functions in opencv that can filtering the multi-dimensional array?
You can use separable kernel filters for make anydimentional filter.
If you are using OpenCV, you could try this for a 3 Dimensional MatND:
void Smooth3DHist(cv::MatND &hist, const int& kernDimension)
{
assert(hist.dims == 3);
int x_size = hist.size[0];
int y_size = hist.size[1];
int z_size = hist.size[2];
int xy_size = x_size*y_size;
cv::Mat kernal = cv::getGaussianKernel(kernDimension, -1, CV_32F);
// Filter XY dimensions for every Z
for (int z = 0; z < z_size; z++)
{
float *ind = (float*)hist.data + z * xy_size; // sub-matrix pointer
cv::Mat subMatrix(2, hist.size, CV_32F, ind);
cv::sepFilter2D(subMatrix, subMatrix, CV_32F, kernal.t(), kernal, Point(-1,-1), 0.0, cv::BORDER_REPLICATE);
}
// Filter Z dimension
float* kernGauss = (float *)kernal.data;
unsigned kernSize = kernal.total();
int kernMargin = (kernSize - 1)/2;
float* lineBuffer = new float[z_size + 2*kernMargin];
for (int y = 0; y < y_size; y++)
{
for (int x = 0; x < x_size; x++)
{
// Copy along Z dimension into a line buffer
float* z_ptr = (float*)hist.data + y * x_size + x;//same as hist.ptr<float>(0, y, x)
for (int z = 0; z < z_size; z++, z_ptr += xy_size)
{
lineBuffer[z + kernMargin] = *z_ptr;
}
// Replicate borders
for (int m = 0; m < kernMargin; m++)
{
lineBuffer[m] = lineBuffer[kernMargin];// replicate left side
lineBuffer[z_size + 2*kernMargin - 1 - m] = lineBuffer[kernMargin + z_size - 1];//replicate right side
}
// Filter line buffer 1D - convolution
z_ptr = (float*)hist.data + y * x_size + x;
for (int z = 0; z < z_size; z++, z_ptr += xy_size)
{
*z_ptr = 0.0f;
for (unsigned k = 0; k < kernSize; k++)
{
*z_ptr += lineBuffer[z+k]*kernGauss[k];
}
}
}
}
delete [] lineBuffer;
}