Call OpenCV CvSVMParams in Objective-C - c++

I want to implement SVM algorithm using OpenCV in iOS but can not call some methods in Objective-C. How to call OpenCV CvSVMParams in Objective-C. When I tried this it shows error 'Unknown type name CvSVMParams'
Edited: I understand my mistake I was using old version of OpenCV now I fixed that. But compiler says
OpenCV Error: Assertion failed (samples.cols == var_count && samples.type() == CV_32F) in predict, file /Volumes/Linux/builds/precommit_ios/opencv/modules/ml/src/svm.cpp, line 1919
libc++abi.dylib: terminating with uncaught exception of type cv::Exception: /Volumes/Linux/builds/precommit_ios/opencv/modules/ml/src/svm.cpp:1919: error: (-215) samples.cols == var_count && samples.type() == CV_32F in function predict
#import "CustomObject.h"
#import <opencv2/opencv.hpp>
#import <CoreGraphics/CoreGraphics.h>
#import <UIKit/UIKit.h>
using namespace cv;
#implementation CustomObject
- (void) supportVectorMachine {
float labels[10] = { 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0 };
cv::Mat labelsMat(10, 1, CV_32FC1, labels);
float trainingData[10][2] = { { 100, 10 }, { 150, 10 }, { 600, 200 }, { 600, 10 }, { 10, 100 }, { 455, 10 }, { 345, 255 }, { 10, 501 }, { 401, 255 }, { 30, 150 } };
cv::Mat trainDataMat(10, 2, CV_32FC1, trainingData);
//opencv 3.0
Ptr<ml::SVM> svm = ml::SVM::create();
// edit: the params struct got removed,
// we use setter/getter now:
svm->setType(ml::SVM::C_SVC);
svm->setKernel(ml::SVM::LINEAR);
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));
Ptr<TrainData> td = TrainData::create(trainDataMat, ROW_SAMPLE, labelsMat);
//Create test features
float testData[2] = { 150, 15 };
cv::Mat testDataMat(2, 1, CV_32FC1, testData);
//Predict the class labele for test data sample
float predictLable = svm->predict(testDataMat);
NSLog(#"%f", predictLable);
}
end

Problem was solved. I forgot to call train method for Trained Data and change labels from float to integers.
- (void) supportVectorMachine {
int labels[10] = {1, 1, 1};
cv::Mat labelsMat(10, 1, CV_32S, labels);
float trainingData[3][3] = { { 84, 191, 19 }, { 24, 186, 17}, { 22, 157, 21} };
//float trainingData[10][1] = { 100, 150, 600, 600, 100, 455, 345, 501, 401, 150};
cv::Mat trainDataMat(3, 3, CV_32FC1, trainingData);
//opencv 3.0
Ptr<ml::SVM> svm = ml::SVM::create();
// edit: the params struct got removed,
// we use setter/getter now:
svm->setType(ml::SVM::C_SVC);
svm->setKernel(ml::SVM::LINEAR);
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));
svm->setGamma(3.0);
Ptr<TrainData> td = TrainData::create(trainDataMat, ROW_SAMPLE, labelsMat);
svm->train(td);
//Create test features
float testData[1] = {500};
cv::Mat testDataMat(1, 1, CV_32FC1, testData);
//Predict the class labele for test data sample
float predictLable = svm->predict(testDataMat);
NSLog(#"%f", predictLable);
}

Related

How can I use SVM in OpenCV with 2 channel Mat?

I want to classify coordinate set using SVM in OpenCV.
For example,
label 1 for { 70, 80 }, { 94, 90 }, { 70, 85 }
label -1 for { 98, 89 },{ 99, 94 }, { 91, 87 }
In the example offered by OpenCV, only one coordinate data is
used for one node. However, I want to use coordinate set as
one node. I tried this using CV_32FC2 Mat for test.
However, I think it can not be used for training of SVM. Error happened.
Somebody know how to use SVM for this case?
int record[3][3][2] = {
{
{ 70, 80 },
{ 94, 90 },
{ 70, 85 }
},
{
{ 83, 90 },
{ 95, 60 },
{ 90, 82 }
},
{
{ 98, 89 }, // 3반 학생1의 성적
{ 99, 94 }, // 3반 학생2의 성적
{ 91, 87 } // 3반 학생3의 성적
}
};
int labels[3] = { 1, -1, -1 };
Mat trainingDataMat(3, 3, CV_32FC2, record);
Mat labelsMat(3, 1, CV_32SC1, labels);
Ptr<SVM> svm = SVM::create();
svm->setType(SVM::C_SVC);
svm->setKernel(SVM::LINEAR);
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));
//! [init]
//! [train]
svm->train(trainingDataMat, ROW_SAMPLE, labelsMat);
Why did you choose size of trainingDataMat to be 3x3?
Have a look at this tutorial code, which uses 2D points as training data.
http://docs.opencv.org/2.4/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.html
As you can see, they use only 2 colums:
// Set up training data
float labels[4] = {1.0, -1.0, -1.0, -1.0};
Mat labelsMat(4, 1, CV_32FC1, labels);
float trainingData[4][2] = { {501, 10}, {255, 10}, {501, 255}, {10, 501} };
Mat trainingDataMat(4, 2, CV_32FC1, trainingData);
If you want to use integers instead of floats, you'll have to adjust types.

How to multiply 2 OpenCV mats using a GPU

In OpenCV, I can multiply an RGB 1920 x 1080 mat by a 3 x 3 Mat to change the color composition of my source Mat. Once my source mat is properly shaped, I can use the '*' operator to perform the multiplication. This operator is not available when using a cv::gpu::GpuMat.
My question is how would I format my input source Mat to use cv::gpu::gemm?Can I even use cv::gpu::gemm?
This is the only call that performs matrix multiplication in the OpenCV library from what I can tell. cv::gpu::gemm wants to see a CV_32FC1 , CV_64FC1 type Mat. The type I normally use with the CPU is CV_32FC3.
//sourceMat is CV_32FC3 1920 x 1080 Mat
Mat sourceMat = matFromBuffer(data->bufferA, data->widthA, data->heightA);
//This is the color Matrix
float matrix[3][3] = {{1.057311, -0.204043, 0.055648},
{ 0.041556, 1.875992, -0.969256},
{-0.498535,-1.537150, 3.240479}};
Mat colorMatrixMat = Mat(3, 3, CV_32FC1, matrix).t();
//Color Correct the Mat
Mat linearSourceMat = sourceMat.reshape(1, 1080*1920);
Mat multipliedMatrix = linearSourceMat * colorMatrixMat;
Mat recoloredMat = multipliedMatrix.reshape(3, 1080);
Update:
As a test, I created the test routine:
static int gpuTest(){
float matrix[9] = {1.057311, -0.204043, 0.055648, 0.041556, 1.875992, -0.969256, -0.498535,-1.537150, 3.240479};
Mat matrixMat = Mat(1, 9, CV_32FC1, matrix).t();
cv::gpu::GpuMat gpuMatrixMat;
gpuMatrixMat.upload(matrixMat);
float matrixDest[9] = {1,1,1,1,1,1,1,1,1};
Mat matrixDestMat = Mat(1, 9, CV_32FC1, matrixDest).t();
cv::gpu::GpuMat destMatrixMat;
destMatrixMat.upload(matrixDestMat);
cv::gpu::GpuMat nextMat;
cv::gpu::gemm(gpuMatrixMat, destMatrixMat, 1, cv::gpu::GpuMat(), 0, nextMat);
return 0;
};
and the error I receive is:
OpenCV Error: Assertion failed (src1Size.width == src2Size.height) in gemm, file /Users/myuser/opencv-2.4.12/modules/gpu/src/arithm.cpp, line 109
libc++abi.dylib: terminating with uncaught exception of type cv::Exception: /Users/myuser/opencv-2.4.12/modules/gpu/src/arithm.cpp:109: error: (-215) src1Size.width == src2Size.height in function gemm
Now how can the src1Size.width be equal to src2Size.height? The width and height are different.
Here's a minimum working example using OpenCV 3.1.
#include <opencv2/opencv.hpp>
#include <opencv2/cudaarithm.hpp>
int main()
{
cv::Mat sourceMat = cv::Mat::ones(1080, 1920, CV_32FC3);
//This is the color Matrix
float matrix[3][3] = {
{ 1.057311, -0.204043, 0.055648 }
, { 0.041556, 1.875992, -0.969256 }
, { -0.498535, -1.537150, 3.240479 }
};
cv::Mat colorMatrixMat = cv::Mat(3, 3, CV_32FC1, matrix).t();
cv::Mat linearSourceMat = sourceMat.reshape(1, 1080 * 1920);
cv::Mat multipliedMatrix = linearSourceMat * colorMatrixMat;
try {
cv::Mat dummy, gpuMultipliedMatrix;
// Regular gemm
cv::gemm(linearSourceMat, colorMatrixMat, 1.0, dummy, 0.0, gpuMultipliedMatrix);
// CUDA gemm
// cv::cuda::gemm(linearSourceMat, colorMatrixMat, 1.0, dummy, 0.0, gpuMultipliedMatrix);
std::cout << (cv::countNonZero(multipliedMatrix != gpuMultipliedMatrix) == 0);
} catch (cv::Exception& e) {
std::cerr << e.what();
return -1;
}
}
Note that when the beta parameter to gemm(...) is zero, the third input matrix is ignored (based on the code).
Unfortunately I don't have a build of OpenCV compiled with CUBLAS available to try it, but it should work.
Following is somewhat speculative...
To make this work with OpenCV 2.4, you will need to add a little bit more. Before calling gemm(...), you need to create GpuMat objects and upload the data.
cv::gpu::GpuMat gpuLinSrc, gpuColorMat, dummy, gpuResult;
gpuLinSrc.upload(linearSourceMat);
gpuColorMat.upload(colorMatrixMat);
Then...
cv::gpu::gemm(gpuLinSrc, gpuColorMat, 1.0, cv::gpu::GpuMat(), 0.0, gpuResult);
and finally download the data back from the GPU.
cv::Mat resultFromGPU;
gpuResult.download(resultFromGPU);
Update
Here's a more detailed example to show you what's happening:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <numeric>
#include <vector>
// ============================================================================
// Make a 3 channel test image with 5 rows and 4 columns
cv::Mat make_image()
{
std::vector<float> v(5 * 4);
std::iota(std::begin(v), std::end(v), 1.0f); // Fill with 1..20
cv::Mat seq(5, 4, CV_32FC1, v.data()); // 5 rows, 4 columns, 1 channel
// Create 3 channels, each with different offset, so we can tell them apart
cv::Mat chans[3] = {
seq, seq + 100, seq + 200
};
cv::Mat merged;
cv::merge(chans, 3, merged); // 5 rows, 4 columns, 3 channels
return merged;
}
// Make a transposed color correction matrix.
cv::Mat make_color_mat()
{
float color_in[3][3] = {
{ 0.1f, 0.2f, 0.3f } // Coefficients for channel 0
, { 0.4f, 0.5f, 0.6f } // Coefficients for channel 1
, { 0.7f, 0.8f, 0.9f } // Coefficients for channel 2
};
return cv::Mat(3, 3, CV_32FC1, color_in).t();
}
void print_mat(cv::Mat m, std::string const& label)
{
std::cout << label << ":\n size=" << m.size()
<< "\n channels=" << m.channels()
<< "\n" << m << "\n" << std::endl;
}
// Perform matrix multiplication to obtain result point (r,c)
float mm_at(cv::Mat a, cv::Mat b, int r, int c)
{
return a.at<float>(r, 0) * b.at<float>(0, c)
+ a.at<float>(r, 1) * b.at<float>(1, c)
+ a.at<float>(r, 2) * b.at<float>(2, c);
}
// Perform matrix multiplication to obtain result row r
cv::Vec3f mm_test(cv::Mat a, cv::Mat b, int r)
{
return cv::Vec3f(
mm_at(a, b, r, 0)
, mm_at(a, b, r, 1)
, mm_at(a, b, r, 2)
);
}
// ============================================================================
int main()
{
try {
// Step 1
cv::Mat source_image(make_image());
print_mat(source_image, "source_image");
std::cout << "source pixel at (0,0): " << source_image.at<cv::Vec3f>(0, 0) << "\n\n";
// Step 2
cv::Mat color_mat(make_color_mat());
print_mat(color_mat, "color_mat");
// Step 3
// Reshape the source matrix to obtain a matrix:
// * with only one channel (CV_32FC1)
// * where each row corresponds to a single pixel from source
// * where each column corresponds to a single channel from source
cv::Mat reshaped_image(source_image.reshape(1, source_image.rows * source_image.cols));
print_mat(reshaped_image, "reshaped_image");
// Step 4
cv::Mat corrected_image;
// corrected_image = 1.0 * reshaped_image * color_mat
cv::gemm(reshaped_image, color_mat, 1.0, cv::Mat(), 0.0, corrected_image);
print_mat(corrected_image, "corrected_image");
// Step 5
// Reshape back to the original format
cv::Mat result_image(corrected_image.reshape(3, source_image.rows));
print_mat(result_image, "result_image");
std::cout << "result pixel at (0,0): " << result_image.at<cv::Vec3f>(0, 0) << "\n\n";
// Step 6
// Calculate one pixel manually...
std::cout << "check pixel (0,0): " << mm_test(reshaped_image, color_mat, 0) << "\n\n";
} catch (cv::Exception& e) {
std::cerr << e.what();
return -1;
}
}
// ============================================================================
Step 1
First we create a small test input image:
The image contains 3 channels of float values, i.e. the data type is CV_32FC3. Let's treat the channels as red, green, blue in that order.
The image contains 5 rows of pixels.
The image contains 4 columns of pixels.
Values in each channel are sequential, green = red + 100 and blue = red + 200.
source_image:
size=[4 x 5]
channels=3
[1, 101, 201, 2, 102, 202, 3, 103, 203, 4, 104, 204;
5, 105, 205, 6, 106, 206, 7, 107, 207, 8, 108, 208;
9, 109, 209, 10, 110, 210, 11, 111, 211, 12, 112, 212;
13, 113, 213, 14, 114, 214, 15, 115, 215, 16, 116, 216;
17, 117, 217, 18, 118, 218, 19, 119, 219, 20, 120, 220]
We can print out a single pixel, to make the structure clearer:
source pixel at (0,0): [1, 101, 201]
Step 2
Create a sample colour correction matrix (transposed) such that:
First column contains coefficients used to determine the red value
Second column contains coefficients used to determine the green value
Third column contains coefficients used to determine the blue value
color_mat:
size=[3 x 3]
channels=1
[0.1, 0.40000001, 0.69999999;
0.2, 0.5, 0.80000001;
0.30000001, 0.60000002, 0.89999998]
Sidenote: Color Correction Algorithm
We want to transform source pixel S to pixel T using coefficients C
S = [ sr, sg, sb ]
T = [ tr, tg, tb ]
C = [ cr1, cr2, cr3;
cg1, cg2, cg3;
cb1, cb2, cb3]
Such that
Tr = cr1 * sr + cr2 * sg + cr3 * sb
Tg = cg1 * sr + cg2 * sg + cg3 * sb
Tb = cb1 * sr + cb2 * sg + cb3 * sb
Which can be represented by the following matrix expression
T = S * C_transpose
Step 3
In order to be able to use the above algorithm, we first need to reshape our image into a matrix that:
Contains a single channel, so that value at each point is just a float
Has one pixel per row.
Has 3 columns representing red, green, blue
In this shape, matrix multiplication will mean that each pixel/row from input gets multiplied by the coefficient matrix to determine one pixel/row in the output.
The reshaped matrix looks as follows:
reshaped_image:
size=[3 x 20]
channels=1
[1, 101, 201;
2, 102, 202;
3, 103, 203;
4, 104, 204;
5, 105, 205;
6, 106, 206;
7, 107, 207;
8, 108, 208;
9, 109, 209;
10, 110, 210;
11, 111, 211;
12, 112, 212;
13, 113, 213;
14, 114, 214;
15, 115, 215;
16, 116, 216;
17, 117, 217;
18, 118, 218;
19, 119, 219;
20, 120, 220]
Step 4
We perform the multiplication, for example using gemm, to get the following matrix:
corrected_image:
size=[3 x 20]
channels=1
[80.600006, 171.5, 262.39999;
81.200005, 173, 264.79999;
81.800003, 174.5, 267.20001;
82.400002, 176, 269.60001;
83, 177.5, 272;
83.600006, 179, 274.39999;
84.200005, 180.5, 276.79999;
84.800003, 182, 279.20001;
85.400002, 183.5, 281.60001;
86, 185, 284;
86.600006, 186.5, 286.39999;
87.200005, 188, 288.79999;
87.800003, 189.5, 291.20001;
88.400009, 191, 293.60001;
89, 192.5, 296;
89.600006, 194, 298.39999;
90.200005, 195.50002, 300.79999;
90.800003, 197, 303.20001;
91.400009, 198.5, 305.60001;
92, 200, 308]
Step 5
Now we can reshape the image back to the original shape. The result is
result_image:
size=[4 x 5]
channels=3
[80.600006, 171.5, 262.39999, 81.200005, 173, 264.79999, 81.800003, 174.5, 267.20001, 82.400002, 176, 269.60001;
83, 177.5, 272, 83.600006, 179, 274.39999, 84.200005, 180.5, 276.79999, 84.800003, 182, 279.20001;
85.400002, 183.5, 281.60001, 86, 185, 284, 86.600006, 186.5, 286.39999, 87.200005, 188, 288.79999;
87.800003, 189.5, 291.20001, 88.400009, 191, 293.60001, 89, 192.5, 296, 89.600006, 194, 298.39999;
90.200005, 195.50002, 300.79999, 90.800003, 197, 303.20001, 91.400009, 198.5, 305.60001, 92, 200, 308]
Let's have a look at one pixel from the result:
result pixel at (0,0): [80.6, 171.5, 262.4]
Step 6
Now we can double check our result by performing the appropriate calculations manually (functions mm_test and mm_at).
check pixel (0,0): [80.6, 171.5, 262.4]

HSV color detection with OpenCV

The "red" color-detection is not working yet. The following code is supposed to detect a red bar from an input-image and return a mask-image showing a white bar at the corresponding location.
The corresponding HSV-values of the "red" bar in the inputRGBimage are : H = 177, S = 252, V = 244
cv::Mat findColor(cv::Mat inputRGBimage) {
cv::Mat imageHSV(inputRGBimage.rows, inputRGBimage.cols, CV_8UC3);
cv::Mat imgThreshold(inputRGBimage.rows, inputRGBimage.cols, CV_8UC1);
// convert input-image to HSV-image
cvtColor(inputRGBimage, imageHSV, cv::COLOR_BGR2HSV);
// for red: (H < 14)
// cv::inRange(imageHSV, cv::Scalar(0, 53, 185, 0), cv::Scalar(14, 255, 255, 0), imgThreshold);
// or (H > 165) (...closing HSV-circle)
cv::inRange(imageHSV, cv::Scalar(165, 53, 185, 0), cv::Scalar(180, 255, 255, 0), imgThreshold);
return imgThreshold;
}
The two images below show the inputRGBimage (top) and the returned imgThreshold (bottom). As you can see, the mask is not showing the white bar at the expected color "red" but shows it for some unknown reason at the "blue" bar. Why ????
The following change of the cv::inRange line of code (i.e. H > 120) and its result again illustrates that the color detection is not actually acting as expected :
// or (H > 120) (...closing HSV-circle)
cv::inRange(imageHSV, cv::Scalar(120, 53, 185, 0), cv::Scalar(180, 255, 255, 0), imgThreshold);
As a third example: (H > 100):
// or (H > 100) (...closing HSV-circle)
cv::inRange(imageHSV, cv::Scalar(100, 53, 185, 0), cv::Scalar(180, 255, 255, 0), imgThreshold);
Why the unexpected order of colors in my 3 code-examples (decreasing the H-value from 165 to 100) showing mask orders of "blue->violet->red->orange" instead of the actually expected HSV-wheel rough order of "red->violet->blue->green->yellow->orange" ?????
HSV in OpenCV has ranges:
0 <= H <= 180,
0 <= S <= 255,
0 <= V <= 255, (not quite like in the illustrating graphic above - but the order of colors should be the same for OpenCV HSV-colors - or not ???)
Make sure that the image uses the channel order B, G, R. Also, for the color red you need check two ranges of values, one around H=0 and the other around H=180. You could try this function:
cv::Mat findColor(const cv::Mat & inputBGRimage, int rng=15)
{
// Make sure that your input image uses the channel order B, G, R (check not implemented).
cv::Mat input = inputBGRimage.clone();
cv::Mat imageHSV;//(input.rows, input.cols, CV_8UC3);
cv::Mat imgThreshold, imgThreshold0, imgThreshold1;//(input.rows, input.cols, CV_8UC1);
assert( ! input.empty() );
// convert input-image to HSV-image
cv::cvtColor( input, imageHSV, cv::COLOR_BGR2HSV );
// In the HSV-color space the color 'red' is located around the H-value 0 and also around the
// H-value 180. That is why you need to threshold your image twice and the combine the results.
cv::inRange(imageHSV, cv::Scalar( 0, 53, 185, 0), cv::Scalar(rng, 255, 255, 0), imgThreshold0);
if ( rng > 0 )
{
cv::inRange(imageHSV, cv::Scalar(180-rng, 53, 185, 0), cv::Scalar(180, 255, 255, 0), imgThreshold1);
cv::bitwise_or( imgThreshold0, imgThreshold1, imgThreshold );
}
else
{
imgThreshold = imgThreshold0;
}
return imgThreshold;
}
Good luck! :)

JPEG Compression using OpenCV

I'm trying to perform a basic JPEG Compression (DCT + quantization + IDCT) using OpenCV not using entropy-encoding/Huffman-coding. The problem is that after I decompress the compressed image, it is not even close in appearance to the original one.
I'm following these tutorials:
Basic JPEG Compressing/Decompressing Simulation
Basic JPEG Compression using OpenCV
Following are the 3 images (original, compressed and decompressed images):
I'm using the following matrix to luminance and chrominance:
double dataLuminance[8][8] = {
{16, 11, 10, 16, 24, 40, 51, 61},
{12, 12, 14, 19, 26, 58, 60, 55},
{14, 13, 16, 24, 40, 57, 69, 56},
{14, 17, 22, 29, 51, 87, 80, 62},
{18, 22, 37, 56, 68, 109, 103, 77},
{24, 35, 55, 64, 81, 104, 113, 92},
{49, 64, 78, 87, 103, 121, 120, 101},
{72, 92, 95, 98, 112, 100, 103, 99}
};
double dataChrominance[8][8] = {
{17, 18, 24, 27, 99, 99, 99, 99},
{18, 21, 26, 66, 99, 99, 99, 99},
{24, 26, 56, 99, 99, 99, 99, 99},
{47, 66, 99, 99, 99, 99, 99, 99},
{99, 99, 99, 99, 99, 99, 99, 99},
{99, 99, 99, 99, 99, 99, 99, 99},
{99, 99, 99, 99, 99, 99, 99, 99},
{99, 99, 99, 99, 99, 99, 99, 99}
};
// EDIT 1: #Micka told about the problem of using imread/imwrite, so I edited my code to use the compressed image directly from my program.
The compression method is:
void ImageCompression::compression(){
// Getting original image size
int height = imgOriginal.size().height;
int width = imgOriginal.size().width;
// Converting image color
Mat imgColorConverted;
cvtColor(imgOriginal, imgColorConverted, CV_BGR2YCrCb);
// Transforming 2D Array in Image Matrix
Mat luminance = Mat(8,8, CV_64FC1, &dataLuminance);
Mat chrominance = Mat(8,8, CV_64FC1, &dataChrominance);
cout << "Luminance: " << luminance << endl << endl;
cout << "Chrominance" << chrominance << endl << endl;
// Splitting the image into 3 planes
vector<Mat> planes;
split(imgColorConverted, planes);
// Downsampling chrominance
// Resizing to 1/4 of original image
resize(planes[1], planes[1], Size(width/2, height/2));
resize(planes[2], planes[2], Size(width/2, height/2));
// Resizing to original image size
resize(planes[1], planes[1], Size(width, height));
resize(planes[2], planes[2], Size(width, height));
// Dividing image in blocks 8x8
for ( int i = 0; i < height; i+=8 ){
for( int j = 0; j < width; j+=8 ){
// For each plane
for( int plane = 0; plane < imgColorConverted.channels(); plane++ ){
// Creating a block
Mat block = planes[plane](Rect(j, i, 8, 8));
// Converting the block to float
block.convertTo( block, CV_64FC1 );
// Subtracting the block by 128
subtract( block, 128.0, block );
// DCT
dct( block, block );
// Applying quantization
if( plane == 0 ){
divide( block, luminance, block );
}
else {
divide( block, chrominance, block );
}
// Converting it back to unsigned int
block.convertTo( block, CV_8UC1 );
// Copying the block to the original image
block.copyTo( planes[plane](Rect(j, i, 8, 8)) );
}
}
}
merge( planes, finalImage );
}
And my decompression method:
ImageCompression::decompression{
// Getting the size of the image
int height = finalImage.size().height;
int width = finalImage.size().width;
// Transforming 2D Array in Image Matrix
Mat luminance = Mat(8,8, CV_64FC1, &dataLuminance);
Mat chrominance = Mat(8,8, CV_64FC1, &dataChrominance);
// Splitting the image into 3 planes
vector<Mat> planes;
split(finalImage, planes);
// Dividing the image in blocks 8x8
for ( int i = 0; i < height; i+=8 ){
for( int j = 0; j < width; j+=8 ){
// For each plane
for( int plane = 0; plane < finalImage.channels(); plane++ ){
// Creating a block
Mat block = planes[plane](Rect(j, i, 8, 8));
// Converting the block to float
block.convertTo( block, CV_64FC1 );
// Applying dequantization
if( plane == 0 ){
multiply( block, luminance, block );
}
else {
multiply( block, chrominance, block );
}
// IDCT
idct( block, block );
// Adding 128 to the block
add( block, 128.0, block );
// Converting it back to unsigned int
block.convertTo( block, CV_8UC1 );
// Copying the block to the original image
block.copyTo( planes[plane](Rect(j, i, 8, 8)) );
}
}
}
merge(planes, finalImage);
cvtColor( finalImage, finalImage, CV_YCrCb2BGR );
imshow("Decompressed image", finalImage);
waitKey(0);
imwrite(".../finalResult.jpg", finalImage);
}
Does someone have any idea of why I'm getting that resulting image?
Thank you.
You need to add 128 back to the block before converting it back to unsigned int and then subtract it again in decompression.
add(block, 128.0, block);
// Converting it back to unsigned int
block.convertTo(block, CV_8UC1);
.
// Converting the block to float
block.convertTo(block, CV_64FC1);
subtract(block, 128.0, block);

OpencCV: calcOpticalFlowSF function

I try to use the calcOpticalFlowSF() function, but when I launch it, the programm doesn't repond, here the part of the code that use it:
frame1 = cv::imread("frame10.png");
frame2 = cv::imread("frame11.png");
if (frame1.empty()) {
cout<<"could not read image oldori"<<endl;
return;
}
if (frame2.empty()) {
cout<<"could not read image ori"<<endl;
return;
}
if (frame1.rows != frame2.rows && frame1.cols != frame2.cols) {
cout<<"images should be of equal sizes "endl;
return;
}
if (frame1.type() != 16 || frame2.type() != 16) {
cout<<"images should be of equal type CV_8UC3")endl;
return;
}
cv::Mat flow;
cv::calcOpticalFlowSF(frame1, frame2, flow, 2, 2, 4);
// calcOpticalFlowSF(frame1, frame1, // doesn't work too.
// flow,
// 3, 2, 4, 4.1, 25.5, 18, 55.0, 25.5, 0.35, 18, 55.0, 25.5, 10);
I know that the error come from the function calcOpticalFlowSF, because if I comment it, the programm works. I use the same pictures as they use in the SimpleFlow demo. If you look here:How to get cv::calcOpticalFlowSF to work? it seems that he got no problem with the function itself...
Do you have an idea why it doesn't work?
thanks,
best regards.
make sure the image is 3-channel, is better to see the docs here
#if 1
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <stdlib.h>
#include <opencv2/opencv.hpp>
#include "opencv2/optflow.hpp"
#include "opencv2/highgui.hpp"
using namespace std;
using namespace cv;
using namespace cv::optflow;//calcOpticalFlowSF 's namespace
const size_t choice = 2 ;
// choice
// 1 2 3
// calcOpticalFlowFarneback calcOpticalFlowSF calcOpticalFlowPyrLK
void drawOptFlowMap(const Mat& flow, Mat& cflowmap, int step, const Scalar& color) {
// cflowmap is the pre frame with the line of Optical Flow
// flow is a xxx array, store float point
// store the delta of x,y
// step is every step pixel
for (int y = 0; y < cflowmap.rows; y += step)
for (int x = 0; x < cflowmap.cols; x += step)
{
const Point2f& fxy = flow.at< Point2f>(y, x);
line(cflowmap, Point(x, y), Point(cvRound(x + fxy.x), cvRound(y + fxy.y)),
color);
circle(cflowmap, Point(cvRound(x + fxy.x), cvRound(y + fxy.y)), 1, color, -1);
}
}
void drawOptFlowMap(Mat& cflowmap, int step, const Scalar& color, vector<Point2f> &retPts) {
// same as above, retPts is the next frame point
auto it = retPts.begin();
for (int y = 0; y < cflowmap.rows; y += step)
for (int x = 0; x < cflowmap.cols; x += step)
{
line(cflowmap, Point(x, y), *it, color);
circle(cflowmap, *it, 1, color, -1);
it++;
}
}
int main(int argc, char *argv[])
{
Mat flow;//flow = aft - pre
Mat pre = imread("1hf.png", IMREAD_COLOR);
Mat aft = imread("2hf.png", IMREAD_COLOR);// CV_LOAD_IMAGE_GRAYSCALE gray ; IMREAD_COLOR color
if (pre.empty() || aft.empty()){
printf("Unable to load the image");
return 1;
}
Mat cflow = pre; Mat cflow2 = aft;// store the 3-channel mat of frame, cflow is for show color with line
cvtColor(pre, pre, CV_BGR2GRAY);
cvtColor(aft, aft, CV_BGR2GRAY);
//below parameter of calcOpticalFlowPyrLK
vector<Point2f> prePts;
size_t step = 10;
vector<Point2f> nextPts(pre.rows * pre.cols);
vector<uchar> status;
vector<float> err;
TermCriteria termcrit(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, 0.03);
switch (choice)
{
case 1:// calcOpticalFlowFarneback
calcOpticalFlowFarneback(pre, aft, flow, 0.5, 3, 15, 3, 5, 1.2, 0); // info in the flow; note that input mat should in 1-channel
drawOptFlowMap(flow, cflow, 10, CV_RGB(0, 255, 0)); break;
case 2:// calcOpticalFlowSF
calcOpticalFlowSF(cflow, cflow2,
flow,
3, 2, 4, 4.1, 25.5, 18, 55.0, 25.5, 0.35, 18, 55.0, 25.5, 10);// info in the flow; note that input mat should in 3-channel
drawOptFlowMap(flow, cflow, 10, CV_RGB(0, 255, 0)); break;
case 3:// calcOpticalFlowPyrLK
for (int y = 0; y < pre.rows; y += step)
for (int x = 0; x < pre.cols; x += step)
{
prePts.push_back(Point(x, y));
}
// above get a point vector in step
calcOpticalFlowPyrLK(pre, aft, prePts, nextPts, status, err, Size(31, 31), 3, termcrit, 0, 0.001);// info in the flow; note that input mat should in 1-channel
drawOptFlowMap(cflow, step, CV_RGB(0, 255, 0), nextPts);
break;
default:
break;
}
imshow("pre", pre);
imshow("after", aft);
//cflow is the pre with OpticalFlow line
imshow("pre with OpticalFlow line", cflow);
waitKey(0);
return 0;
}
#endif