This question already has answers here:
How to fill OpenCV image with one solid color?
(9 answers)
Closed 4 years ago.
I am new in OpenCV. I have a image what I want is to change the color of each and every pixel of image with any single color.
I found that code but when I run this part of code then a exception is generated.
for (i = 0;i < img1.rows;i++) {
for (j = 0;j < img1.cols;j++) {
img1.at<Vec3b>(i, j) = Vec3b(255, 105, 240);
}
}
Can anyone please tell me the solution.
Or what I found is that this take a lot of time for the conversion So if their is any other approach then please tell me.
// Make a 3 channel image
cv::Mat img(480,640,CV_8UC3);
// Fill entire image with cyan (blue and green)
img = cv::Scalar(255,255,0);
You can use Mat::operator() to select the roi and then assign a value to it.
void main()
{
cv::Mat img = cv::Mat::ones(5, 5, CV_8UC3);
img({2, 4}, {2, 4}) = cv::Vec3b(7, 8, 9);
cout << img;
}
This will print
[ 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0;
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0;
1, 0, 0, 1, 0, 0, 7, 8, 9, 7, 8, 9, 1, 0, 0;
1, 0, 0, 1, 0, 0, 7, 8, 9, 7, 8, 9, 1, 0, 0;
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0]
To fill image with single color, use rectangle with CV_FILLED argument.
(there might be some reasons for exception - image was not created, wrong pixel format etc - it is hard to diagnose a problem with given information)
What is the safest way to add integer into array value without having to call the array value ? In this case I would rather not to call the array value because the array is inside a nested loop and the loop itself can repeat for thousands times.
For example dots[1] = I want to add value of this array with 3. Here's my sample code :
void box(const Mat &dots, Mat &newdots, int rows, int cols)
{
for (int i = 0; i < dots.rows; i++) {
for (int j = 0; j < dots.cols; j++) {
newdots.at<Vec3b>(i, j)[0] = dots.at<Vec3b>(i, j)[0];
newdots.at<Vec3b>(i, j)[1] = dots.at<Vec3b>(i, j)[1]; //add this with 3
newdots.at<Vec3b>(i, j)[2] = dots.at<Vec3b>(i, j)[2]; //add this with 5
}
}
Is it possible ? Any suggestion how to do it ? Thanks.
This simplest way is to use cv::add, which overloads the + operator for the Mat class:
// Create a Mat of all 0's
cv::Mat dots = cv::Mat(5, 4, CV_8UC3, cv::Scalar(0,0,0));
std::cout << "dots:\n" << dots << std::endl;
// Add 0 to the B channel, 3 to the G channel, and 5 to R
cv::Mat newdots = dots + cv::Scalar(0, 3, 5);
std::cout << "newdots:\n" << newdots << std::endl;
Result:
dots:
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
newdots:
[ 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5;
0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5;
0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5;
0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5;
0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5]
Note that dots += Scalar(0,3,5) also works if you just want to modify the original Mat.
Is there an efficient way to resize an image in OpenCV without using any interpolation? Instead of the conventional "resize" I would like my image to remap the pixels into a larger image but pad everything else with 0.
e.g. to scale up img1 below 2x to img2:
img1 = [ 1, 2, 3,
4, 5, 6,
7, 8, 9 ]
cv::resize(img1, img2, cv::Size(6, 6));
img2 = [ 1, 0, 2, 0, 3, 0,
0, 0, 0, 0, 0, 0,
4, 0, 5, 0, 6, 0,
0, 0, 0, 0, 0, 0,
7, 0, 8, 0, 9, 0,
0, 0, 0, 0, 0, 0 ]
I know the obvious way is to just use a for loop, but I'm wondering if there is a more efficient way using an OpenCV call?
One option that comes to mind would be to use cv::resize with INTER_NEAREST and then mask out the unwanted pixels.
Example:
#include <opencv2/opencv.hpp>
#include <cstdint>
#include <iostream>
int main()
{
cv::Mat m1((cv::Mat_<uint8_t>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9));
std::cout << "Input:\n" << m1 << "\n\n";
cv::Mat mask((cv::Mat_<uint8_t>(2, 2) << 255, 0, 0, 0));
mask = cv::repeat(mask, m1.rows, m1.cols);
std::cout << "Mask:\n" << mask << "\n\n";
cv::Mat m2;
cv::resize(m1, m2, cv::Size(), 2, 2, cv::INTER_NEAREST);
std::cout << "Resized:\n" << m2 << "\n\n";
cv::bitwise_and(m2, mask, m2);
std::cout << "Masked:\n" << m2 << "\n\n";
}
Console output:
Input:
[ 1, 2, 3;
4, 5, 6;
7, 8, 9]
Mask:
[255, 0, 255, 0, 255, 0;
0, 0, 0, 0, 0, 0;
255, 0, 255, 0, 255, 0;
0, 0, 0, 0, 0, 0;
255, 0, 255, 0, 255, 0;
0, 0, 0, 0, 0, 0]
Resized:
[ 1, 1, 2, 2, 3, 3;
1, 1, 2, 2, 3, 3;
4, 4, 5, 5, 6, 6;
4, 4, 5, 5, 6, 6;
7, 7, 8, 8, 9, 9;
7, 7, 8, 8, 9, 9]
Masked:
[ 1, 0, 2, 0, 3, 0;
0, 0, 0, 0, 0, 0;
4, 0, 5, 0, 6, 0;
0, 0, 0, 0, 0, 0;
7, 0, 8, 0, 9, 0;
0, 0, 0, 0, 0, 0]
Update
If we eliminate parts of Miki's code that are unnecessary for our specific scenario, we pretty much reduce it to a simple loop.
Doing some quick comparisons, this turns out to be somewhat faster.
#include <opencv2/opencv.hpp>
#include <chrono>
#include <cstdint>
#include <iostream>
cv::Mat resize_1(cv::Mat image)
{
cv::Mat result(cv::Mat::zeros(image.rows * 2, image.cols * 2, CV_8UC1));
for (int ra(0); ra < image.rows; ++ra) {
for (int ca = 0; ca < image.cols; ++ca) {
result.at<uint8_t>(ra * 2, ca * 2) = image.at<uint8_t>(ra, ca);
}
}
return result;
}
cv::Mat resize_2(cv::Mat image)
{
cv::Mat mask((cv::Mat_<uint8_t>(2, 2) << 255, 0, 0, 0));
mask = cv::repeat(mask, image.rows, image.cols);
cv::Mat result;
cv::resize(image, result, cv::Size(), 2, 2, cv::INTER_NEAREST);
cv::bitwise_and(result, mask, result);
return result;
}
template<typename T>
void timeit(T f)
{
using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::microseconds;
cv::Mat m1((cv::Mat_<uint8_t>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9));
m1 = cv::repeat(m1, 1024, 1024);
high_resolution_clock::time_point t1 = high_resolution_clock::now();
for (uint32_t i(0); i < 256; ++i) {
cv::Mat result = f(m1);
}
high_resolution_clock::time_point t2 = high_resolution_clock::now();
auto duration = duration_cast<microseconds>(t2 - t1).count();
double t_ms(static_cast<double>(duration) / 1000.0);
std::cout
<< "Total = " << t_ms << " ms\n"
<< "Iteration = " << (t_ms / 256) << " ms\n"
<< "FPS = " << (256 / t_ms * 1000.0) << "\n";
}
int main()
{
timeit(&resize_1);
timeit(&resize_2);
}
Timing:
resize_1
Total = 6344.86 ms
Iteration = 24.7846 ms
FPS = 40.3476
resize_2
Total = 7271.31 ms
Iteration = 28.4036 ms
FPS = 35.2068
Update 2
Parallelized version:
class ResizeInvoker : public cv::ParallelLoopBody
{
public:
ResizeInvoker(cv::Mat const& src, cv::Mat& dst)
: image(src)
, result(dst)
{
}
void operator()(const cv::Range& range) const
{
for (int y(range.start); y < (range.end); ++y) {
for (int x(0); x < image.cols; ++x) {
result.at<uint8_t>(y * 2, x * 2) = image.at<uint8_t>(y, x);
}
}
}
cv::Mat const& image;
cv::Mat& result;
};
cv::Mat resize_3(cv::Mat image)
{
cv::Mat result(cv::Mat::zeros(image.rows * 2, image.cols * 2, CV_8UC1));
ResizeInvoker loop_body(image, result);
cv::parallel_for_(cv::Range(0, image.rows)
, loop_body
, result.total() / (double)(1 << 16));
return result;
}
Timing:
resize_3
Total = 3876.63 ms
Iteration = 15.1431 ms
FPS = 66.0367
Update 3
We can do a little better if we use raw pointers in the invoker:
void operator()(const cv::Range& range) const
{
for (int y(range.start); y < (range.end); ++y) {
uint8_t* D = result.data + result.step * y * 2;
uint8_t const* S = image.data + image.step * y;
for (int x(0); x < image.cols; ++x) {
D[x * 2] = S[x];
}
}
}
Timing:
Total = 3387.87 ms
Iteration = 13.2339 ms
FPS = 75.5636
You can use the Kronecker product of your image and a pattern like:
1, 0
0, 0
The result is:
Input:
[1, 2, 3;
4, 5, 6;
7, 8, 9]
Output:
[1, 0, 2, 0, 3, 0;
0, 0, 0, 0, 0, 0;
4, 0, 5, 0, 6, 0;
0, 0, 0, 0, 0, 0;
7, 0, 8, 0, 9, 0;
0, 0, 0, 0, 0, 0]
Code:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
Mat1b kron(const Mat1b& A, const Mat1b& B)
{
Mat1b K(A.rows * B.rows, A.cols * B.cols, uchar(0));
for (int ra = 0; ra < A.rows; ++ra)
{
for (int ca = 0; ca < A.cols; ++ca)
{
K(Range(ra*B.rows, (ra + 1)*B.rows), Range(ca*B.cols, (ca + 1)*B.cols)) = B.mul(A(ra, ca));
}
}
return K;
}
int main()
{
Mat1b img = (Mat1b(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
std::cout << "Input:\n" << img << "\n\n";
// Define the pattern
Mat1b pattern = (Mat1b(2, 2) << 1, 0,
0, 0);
Mat1b out = kron(img, pattern);
std::cout << "Output:\n" << out << "\n\n";
return 0;
}
OpenCV doesn't have the Kronecker product implemented, so you need to write a custom function. For a more general implementation that works with all data types (1 channel) have a look here.
I found that #Dan Masek approach is faster. This is because my kron implementation is not optimized. I expect this approach to work pretty well with a good implementation.
Thought of sharing the following approach as it is a bit different. I don't know how efficient this would be compared to other methods. At least you can use opencv calls without any looping and easily use arbitrary scale factors for x and y.
First convert your image to floating point type, then scale it using warpAffine (use linear interpolation). Resize the same image using nearest-neighbor method. Compare the two resulting images element-wise to get a mask. Use this mask to copy the relevant elements from any of the result images.
Here's the code and some results I get:
uchar data[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Mat im(3, 3, CV_8U, data);
im.convertTo(im, CV_32F);
// x and y scale
int xscale = 2, yscale = 2;
Size size(im.cols * xscale, im.rows * yscale);
float tr[] = {xscale, 0, 0, 0, yscale, 0};
Mat m(2, 3, CV_32F, tr); // transformation matrix
Mat resized1, resized2;
warpAffine(im, resized1, m, size); // affine scaling with linear interpolation
resize(im, resized2, size, 0, 0, INTER_NEAREST); // resize with nearest neighbor
// get the mask
Mat resized = resized1 == resized2;
// copy the pixels
resized1.copyTo(resized, resized);
cout << "image:\n" << im << endl;
cout << "M:\n" << m << endl;
cout << "affine(scaled):\n" << resized1 << endl;
cout << "resized:\n" << resized2 << endl;
cout << "mask:\n" << resized << endl;
cout << "output:\n" << resized << endl;
For xscale = 2, yscale = 2
image:
[1, 2, 3;
4, 5, 6;
7, 8, 9]
M:
[2, 0, 0;
0, 2, 0]
affine(scaled):
[1, 1.5, 2, 2.5, 3, 1.5;
2.5, 3, 3.5, 4, 4.5, 2.25;
4, 4.5, 5, 5.5, 6, 3;
5.5, 6, 6.5, 7, 7.5, 3.75;
7, 7.5, 8, 8.5, 9, 4.5;
3.5, 3.75, 4, 4.25, 4.5, 2.25]
resized:
[1, 1, 2, 2, 3, 3;
1, 1, 2, 2, 3, 3;
4, 4, 5, 5, 6, 6;
4, 4, 5, 5, 6, 6;
7, 7, 8, 8, 9, 9;
7, 7, 8, 8, 9, 9]
mask:
[1, 0, 2, 0, 3, 0;
0, 0, 0, 0, 0, 0;
4, 0, 5, 0, 6, 0;
0, 0, 0, 0, 0, 0;
7, 0, 8, 0, 9, 0;
0, 0, 0, 0, 0, 0]
output:
[1, 0, 2, 0, 3, 0;
0, 0, 0, 0, 0, 0;
4, 0, 5, 0, 6, 0;
0, 0, 0, 0, 0, 0;
7, 0, 8, 0, 9, 0;
0, 0, 0, 0, 0, 0]
For xscale = 4, yscale = 3
output:
[1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
7, 0, 0, 0, 8, 0, 0, 0, 9, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
It seems to me that using Matrix with Ranges as an L-value (assignment target) should work or not (and if not a compiler error would be nice) but not both depending on the particulars of a legitimate r-value.
cout << "hi mom" << endl;
Mat Img0=Mat::zeros(7,7,CV_8UC1);
Mat Img1=Mat::ones(7,7,CV_8UC1);
cout << Img0 << endl;
cout << Img1 << endl;
Img0(Range::all(), Range::all()) = Img1;
cout << Img0 << endl;
Img0(Range::all(), Range::all()) = 1;
cout << Img0 << endl;
Below is the output from the above. The first two matrix print outs are of Img0 and Img1 as initialized by Mat::zeros and Mat::ones respectively.
The third matrix print out is Img0 again but after
Img0(Range::all(), Range::all()) = Img1;
which I expected would set Img0 to Img1; i.e. all ones; but it's not. It's still all zeros.
The fourth/last matrix print out is the result of
Img0(Range::all(), Range::all()) = 1;
Which has the same L value as the third assignment but it works when a scalar is the Rvalue (unlike the third which as a matrix as the RValue).
Is there some sense in this that I'm missing? Should this r-value distinction behavior be allowed? It seems inconsistent to me.
[0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0]
[1, 1, 1, 1, 1, 1, 1;
1, 1, 1, 1, 1, 1, 1;
1, 1, 1, 1, 1, 1, 1;
1, 1, 1, 1, 1, 1, 1;
1, 1, 1, 1, 1, 1, 1;
1, 1, 1, 1, 1, 1, 1;
1, 1, 1, 1, 1, 1, 1]
[0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0]
[1, 1, 1, 1, 1, 1, 1;
1, 1, 1, 1, 1, 1, 1;
1, 1, 1, 1, 1, 1, 1;
1, 1, 1, 1, 1, 1, 1;
1, 1, 1, 1, 1, 1, 1;
1, 1, 1, 1, 1, 1, 1;
1, 1, 1, 1, 1, 1, 1]
No, this is not a bug.
This line Img0(Range::all(), Range::all()) = Img1; doesn't work as expected because Img0(Range::all(), Range::all()) forms a temporary header that is further assigned to another header, which is Img1. Remember that each of these operations is O(1), that is, no data is copied. Thus, no real assignment happens.
You can realize this effect more clearly by doing this:
(Img0(Range::all(), Range::all()) = Img1) = 2;
cout << Img0 << endl;
cout << Img1 << endl;
If you have understood what I described above, you should be aware of that the code will only change the value of Img1. And the output is:
[0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0, 0]
[2, 2, 2, 2, 2, 2, 2;
2, 2, 2, 2, 2, 2, 2;
2, 2, 2, 2, 2, 2, 2;
2, 2, 2, 2, 2, 2, 2;
2, 2, 2, 2, 2, 2, 2;
2, 2, 2, 2, 2, 2, 2;
2, 2, 2, 2, 2, 2, 2]
Further reading: check out similar effect happened to Mat::row().