Related
First i want to say that i am trying to solve Palindrome Number problem on LeetCode using dart, of course
https://leetcode.com/problems/palindrome-number/
So far I have tried total 6 different method to solve the problem.
And am not very happy to say that all of them leading to time limit exceeded error on LeetCode.. Solutions are perfectly working on terminal because of the less test cases but when submit, and it tried on large number it ends up error.
My 6 solutions based on different classes.each class contain their own implemented solution..
I dont know if the while loop in dart is slow or something wrong with the leetcode
class A {
bool isPalindrome(int x) {
if (x < 0) {
return false;
}
double divisor = 1;
while (x / divisor >= 10) {
divisor *= 10;
}
while (x != 0) {
if ((x / divisor).truncate() != (x % 10).floor()) {
return false;
}
x %= divisor.toInt();
x = (x / 10).floor();
divisor /= 100;
}
return true;
}
}
class B {
bool isPalindrome(int x) {
if (x < 0 || (x % 10 == 0 && x != 0)) {
return false;
}
int revNum = 0;
while (x > revNum) {
revNum = revNum * 10 + x % 10;
x = (x / 10).floor();
}
return (x == revNum || x == revNum / 10);
}
}
class C {
bool isPalindrome(int x) {
if (x < 0) {
return false;
}
int y = 0;
int xx = x;
if (xx > 0) {
y = y * 10 + xx % 10;
xx = (xx / 10).floor();
}
return (y != x);
}
// while (xx > 0) {
// y = y * 10 + xx % 10;
// xx = (xx / 10).floor();
// }
}
class D {
bool isPalindrome(int x) {
if (x.isNegative) return false;
final list = convert(x);
for (var i = 0; i < (list.length ~/ 2); i++) {
if (list[i] != list[list.length - 1 - i]) return false;
}
return true;
}
List convert(int x) {
final List<int> list = <int>[];
while (x > 0) {
list.add(x % 10);
x = x ~/ 10;
}
return list;
}
}
class F {
bool isPalindrome(int x) {
if (x.isNegative) return false;
if (x == 0) return true;
if (x % 10 == 0) return false;
var reverted = 0, iter = x;
while (iter > 0) {
reverted = reverted * 10;
reverted += iter % 10;
iter = iter ~/ 10;
}
return reverted == x;
}
}
class G {
bool isPalindrome(int x) {
if (x.isNegative) return false;
var tens = 10;
while (tens < x) tens *= 10;
tens = tens ~/ 10;
while (x > 10) {
var lastDigit = x % 10;
var firstDigit = x ~/ tens;
if (lastDigit != firstDigit) return false;
x -= firstDigit * tens;
x = x ~/ 10;
tens = tens ~/ 100;
}
return true;
}
}
I'm pretty sure the leetcode site has a problem here.
I don't know what they're using their time on, but it seems impossible to get a correct result without a timeout.
My attempted solutions are:
class Solution2 {
// No intermediate allocations, only number operations.
bool isPalindrome(int x) {
if (x < 0) return false;
if (x < 10) return true;
int r = 0;
do {
var d = x.remainder(10);
x = x ~/ 10;
if (r == x) return true; // Odd length number
r = r * 10 + d;
if (r == x) return true; // Even length number
if (r == 0) return false; // Trailing zero
} while (r < x);
return false;
}
}
class Solution3 {
// Just do toString and check that it's a palindrome.
// Integer.toString is heavily optimized already.
bool isPalindrome(int x) {
if (x < 0) return false;
if (x < 10) return true;
var s = x.toString();
for (var i = 0, j = s.length - 1; i < j; i++, j--) {
if (s.codeUnitAt(i) != s.codeUnitAt(j)) return false;
}
return true;
}
}
If those are not fast enough, I seriously doubt anything will be.
I even considered whether repeatedly dividing by 10 was incredibly slow for some reason, so I switched to dividing by 100 instead, which should halve the divisions.
class Solution {
// Minimize divisions by dividing by 100, and do lookup for the digits.
static const digits =[
0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9,
1, 0, 1, 1, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, 1, 8, 1, 9,
2, 0, 2, 1, 2, 2, 2, 3, 2, 4, 2, 5, 2, 6, 2, 7, 2, 8, 2, 9,
3, 0, 3, 1, 3, 2, 3, 3, 3, 4, 3, 5, 3, 6, 3, 7, 3, 8, 3, 9,
4, 0, 4, 1, 4, 2, 4, 3, 4, 4, 4, 5, 4, 6, 4, 7, 4, 8, 4, 9,
5, 0, 5, 1, 5, 2, 5, 3, 5, 4, 5, 5, 5, 6, 5, 7, 5, 8, 5, 9,
6, 0, 6, 1, 6, 2, 6, 3, 6, 4, 6, 5, 6, 6, 6, 7, 6, 8, 6, 9,
7, 0, 7, 1, 7, 2, 7, 3, 7, 4, 7, 5, 7, 6, 7, 7, 7, 8, 7, 9,
8, 0, 8, 1, 8, 2, 8, 3, 8, 4, 8, 5, 8, 6, 8, 7, 8, 8, 8, 9,
9, 0, 9, 1, 9, 2, 9, 3, 9, 4, 9, 5, 9, 6, 9, 7, 9, 8, 9, 9,
];
bool isPalindrome(int x) {
if (x < 0) return false;
if (x < 10) return true;
int r = 0;
do {
var d = x.remainder(100);
x = x ~/ 100;
var d1 = digits[d * 2];
var d2 = digits[d * 2 + 1];
if (r == x && d1 == d2) return true; // Even length number
if (r == x * 10 + d1) return true; // Odd length number.
r = r * 10 + d2;
if (r == 0) return false; // Trailing zero
if (r == x) return true; // Odd length number
r = r * 10 + d1;
if (r == x) return true; // Even length number
} while (r < x);
return false;
}
}
Still times out.
I am unable to access a Mat of type 16UC(6). Below is the code used to iterate over the Mat.
//6 channel Mat
int cols=1280, rows=720;
Mat mat1=Mat(cols, rows, CV_16UC(6), Scalar::all(0));
Mat grid(Size(cols, rows), CV_16UC2, Scalar::all(0));
//create a grid of numbers - the value of each pixel in the grid
contains the coordinate of the pixel
for (int i = 0; i < grid.rows; ++i) {
for (int j = 0; j < grid.cols; ++j) {
grid.at<Vec2s>(i, j)[0] = (ushort)j;
grid.at<Vec2s>(i, j)[1] = (ushort)i;
}
}
vector<Mat> imgs(2); //create copies of the grid for each image
for(int i=0;i<2;i++){
imgs[i] = grid.clone();
}
//Values in Mat1 are filled with values of imgs[0] and imgs[1] using
// some logic.
int rows=mat1.rows;
int channels=mat1.channels();
int cols=mat1.cols * channels;
uchar* p;
for(int i=0;i<rows;i++){
p=mat1.ptr<uchar>(i);
for(int j=0;j<cols;j+=6){
cout<<"Value 0 :"<<p[j]<<endl;
cout<<"Value 1 :"<<p[j+1]<<endl;
cout<<"Value 2 :"<<p[j+2]<<endl;
cout<<"Value 3 :"<<p[j+3]<<endl;
cout<<"Value 4 :"<<p[j+4]<<endl;
cout<<"Value 5 :"<<p[j+5]<<endl;
}
}
But im getting ^E and ^# as values. When tried casting to (int) I am getting all zeros.
I am able to access the Mat properly using MatIterator. I am not sure where I went wrong, there must be some issue with the Mat ype and the way I am trying to access the value.Can anyone help me in solving the issue.
You have:
grid.at<Vec2s>(i, j)[0] = (ushort)j;
Vec2s is for shorts, but you have Unsigned Short matrix. You should use Vec2w (not sure who came with the w... or why) that is for unsigned short.
This can be rewritten as:
grid.at<cv::Vec2w>(i, j)[0] = static_cast<ushort>(j);
Then, you show uchar values from the 16U matrix... each uchar is 8bit and each pixel is 16bit...
Here is an example of how you can access each pixel with iterators in a CV_16UC(6) matrix.
// create dummy 3x3 matrix
cv::Mat b(3,3,CV_16UC(6));
// use the templated functions begin and end (you may templated with <ushort>
// directly and it will represent the value of each channel of each pixel)
for (auto it = b.begin<cv::Vec<ushort, 6>>(); it != b.end<cv::Vec<ushort, 6>>(); ++it)
{
// assign some values (this part can be skipped if it is already filled)
(*it)[0] = 5;
(*it)[1] = 7;
(*it)[2] = 8;
(*it)[3] = 9;
(*it)[4] = 1;
(*it)[5] = 2;
// Print the Vec<ushort, 6>, OpenCV already has a way to print it
std::cout << *it << std::endl;
}
And the result of this small code is:
[5, 7, 8, 9, 1, 2]
[5, 7, 8, 9, 1, 2]
[5, 7, 8, 9, 1, 2]
[5, 7, 8, 9, 1, 2]
[5, 7, 8, 9, 1, 2]
[5, 7, 8, 9, 1, 2]
[5, 7, 8, 9, 1, 2]
[5, 7, 8, 9, 1, 2]
[5, 7, 8, 9, 1, 2]
Which is what we expect. You may have notice that I used cv::Vec<ushort, 6>, cv::Vec can be templated with any number of channels (probably there is a limit) and any type (I have only tested it with native numeric types). Actually cv::Vec2w or cv::Vec2s are just typedef of cv::Vec and cv::Vec respectively, and you can also create your typedef if you use it all over the code.
using Vec6w = cv::Vec<ushort, 6>;
and then you can replace it in the for loop:
...
for (auto it = b.begin<Vec6w>(); it != b.end<Vec6w>(); ++it)
...
and achieve the same result
I am currently reading this book. The author wrote a code snippet on page 83 in order to (if i understand it correctly) calculate the element-wise power of two matrices. But i think the code doesn't fulfill its purpose because the matrix dst does not contain the element-wise power after the execution.
Here is the original code:
const Mat* arrays[] = { src1, src2, dst, 0 };
float* ptrs[3];
NAryMatIterator it(arrays, (uchar**)ptrs);
for( size_t i = 0; i < it.nplanes; i++, ++it )
{
for( size_t j = 0; j < it.size; j++ )
{
ptrs[2][j] = std::pow(ptrs[0][j], ptrs[1][j]);
}
}
Since the parameter of the constructor or cv::NAryMatIterator is const cv::Mat **, i think change of values in the matrix dst is not allowed.
I tried to assign ptrs[2][j] back in dst but failed to get the correct indices of dst. My questions are as follows:
Is there a convenient method for the matrix element-wise power, like A .^ B in Matlab?
Is there a way to use cv::NAryMatIterator to achieve this goal? If no, then what is the most efficient way to implement it?
You can get this working by converting the src1, src2 and dst to float (CV_32F) type matrices. This is because the code treats them that way in float* ptrs[3];.
An alternative implementation using opencv functions log, multiply and exp is given at the end.
As an example for your 2nd question,
Mat src1 = (Mat_<int>(3, 3) <<
1, 2, 3,
4, 5, 6,
7, 8, 9);
Mat src2 = (Mat_<uchar>(3, 3) <<
1, 2, 3,
1, 2, 3,
1, 2, 3);
Mat dst = (Mat_<float>(3, 3) <<
1, 2, 3,
4, 5, 6,
7, 8, 9);
src1.convertTo(src1, CV_32F);
src2.convertTo(src2, CV_32F);
cout << "before\n";
cout << dst << endl;
const Mat* arrays[] = { &src1, &src2, &dst, 0 };
float* ptrs[3];
NAryMatIterator it(arrays, (uchar**)ptrs);
for( size_t i = 0; i < it.nplanes; i++, ++it )
{
for( size_t j = 0; j < it.size; j++ )
{
ptrs[2][j] = std::pow(ptrs[0][j], ptrs[1][j]);
}
}
cout << "after\n";
cout << dst << endl;
outputs
before
[1, 2, 3;
4, 5, 6;
7, 8, 9]
after
[1, 4, 27;
4, 25, 216;
7, 64, 729]
If you remove the src1.convertTo(src1, CV_32F); or src2.convertTo(src2, CV_32F);, you won't get the desired result. Try it.
If this is a separate function, don't place the convertTo within the function, as it modifies the image representation, that could affect later operations. Instead, use convertTo on temporary Mats, like
Mat src132f, src232f, dst32f;
src1.convertTo(src132f, CV_32F);
src2.convertTo(src132f, CV_32F);
dst.convertTo(dst32f, CV_32F);
pow_mat(&src132f, &src232f, &dst32f); /* or whatever the name */
As for your first question, I'm not aware of such function. But you can try something like
Mat tmp;
cv::log(src1, tmp);
cv::multiply(src2, tmp, dst);
cv::exp(dst, dst);
using the relation that c = a^b is equivalent to c = e^(b.ln(a)). Here, the matrices should have type 32F or 64F. This produces
[1, 4, 27.000002;
4, 25.000002, 216.00002;
6.9999995, 64, 729.00006]
for the example above.
How can I merge images like below into a single image using OpenCV (there can be any number of them both horizontally and vertically)? Is there any built-in solution to do it?
Additional pieces:
Well, it seems that I finished the puzzle:
Main steps:
Compare each pair of images (puzzle pieces) to know the relative position (findRelativePositions and getPosition).
Build a map knowing the relative positions of the pieces (buildPuzzle and builfForPiece)
Create the final collage putting each image at the correct position (final part of buildPuzzle).
Comparison between pieces A and B in step 1 is done checking for similarity (sum of absolute difference) among:
B is NORTH to A: A first row and B last row;
B is SOUTH to A: A last row and B first row;
B is WEST to A : A last column and B first column;
B is EAST to A : A first column and B last column.
Since images do not overlap, but we can assume that confining rows (columns) are quite similar, the key aspect is to use a (ad-hoc) threshold to discriminate between confining pieces or not. This is handled in function getPosition, with threshold parameter threshold.
Here the full code. Please let me know if something is not clear.
#include <opencv2\opencv.hpp>
#include <algorithm>
#include <set>
using namespace std;
using namespace cv;
enum Direction
{
NORTH = 0,
SOUTH,
WEST,
EAST
};
int getPosition(const Mat3b& A, const Mat3b& B, double& cost)
{
Mat hsvA, hsvB;
cvtColor(A, hsvA, COLOR_BGR2HSV);
cvtColor(B, hsvB, COLOR_BGR2HSV);
int threshold = 1000;
// Check NORTH
Mat3b AN = hsvA(Range(0, 1), Range::all());
Mat3b BS = hsvB(Range(B.rows - 1, B.rows), Range::all());
Mat3b AN_BS;
absdiff(AN, BS, AN_BS);
Scalar scoreN = sum(AN_BS);
// Check SOUTH
Mat3b AS = hsvA(Range(A.rows - 1, A.rows), Range::all());
Mat3b BN = hsvB(Range(0, 1), Range::all());
Mat3b AS_BN;
absdiff(AS, BN, AS_BN);
Scalar scoreS = sum(AS_BN);
// Check WEST
Mat3b AW = hsvA(Range::all(), Range(A.cols - 1, A.cols));
Mat3b BE = hsvB(Range::all(), Range(0, 1));
Mat3b AW_BE;
absdiff(AW, BE, AW_BE);
Scalar scoreW = sum(AW_BE);
// Check EAST
Mat3b AE = hsvA(Range::all(), Range(0, 1));
Mat3b BW = hsvB(Range::all(), Range(B.cols - 1, B.cols));
Mat3b AE_BW;
absdiff(AE, BW, AE_BW);
Scalar scoreE = sum(AE_BW);
vector<double> scores{ scoreN[0], scoreS[0], scoreW[0], scoreE[0] };
int idx_min = distance(scores.begin(), min_element(scores.begin(), scores.end()));
int direction = (scores[idx_min] < threshold) ? idx_min : -1;
cost = scores[idx_min];
return direction;
}
void resolveConflicts(Mat1i& positions, Mat1d& costs)
{
for (int c = 0; c < 4; ++c)
{
// Search for duplicate pieces in each column
set<int> pieces;
set<int> dups;
for (int r = 0; r < positions.rows; ++r)
{
int label = positions(r, c);
if (label >= 0)
{
if (pieces.count(label) == 1)
{
dups.insert(label);
}
else
{
pieces.insert(label);
}
}
}
if (dups.size() > 0)
{
int min_idx = -1;
for (int duplicate : dups)
{
// Find minimum cost position
Mat1d column = costs.col(c);
min_idx = distance(column.begin(), min_element(column.begin(), column.end()));
// Keep only minimum cost position
for (int ir = 0; ir < positions.rows; ++ir)
{
int label = positions(ir, c);
if ((label == duplicate) && (ir != min_idx))
{
positions(ir, c) = -1;
}
}
}
}
}
}
void findRelativePositions(const vector<Mat3b>& pieces, Mat1i& positions)
{
positions = Mat1i(pieces.size(), 4, -1);
Mat1d costs(pieces.size(), 4, DBL_MAX);
for (int i = 0; i < pieces.size(); ++i)
{
for (int j = i + 1; j < pieces.size(); ++j)
{
double cost;
int pos = getPosition(pieces[i], pieces[j], cost);
if (pos >= 0)
{
if (costs(i, pos) > cost)
{
positions(i, pos) = j;
costs(i, pos) = cost;
switch (pos)
{
case NORTH:
positions(j, SOUTH) = i;
costs(j, SOUTH) = cost;
break;
case SOUTH:
positions(j, NORTH) = i;
costs(j, NORTH) = cost;
break;
case WEST:
positions(j, EAST) = i;
costs(j, EAST) = cost;
break;
case EAST:
positions(j, WEST) = i;
costs(j, WEST) = cost;
break;
}
}
}
}
}
resolveConflicts(positions, costs);
}
void builfForPiece(int idx_piece, set<int>& posed, Mat1i& labels, const Mat1i& positions)
{
Point pos(-1, -1);
// Find idx_piece on grid;
for (int r = 0; r < labels.rows; ++r)
{
for (int c = 0; c < labels.cols; ++c)
{
if (labels(r, c) == idx_piece)
{
pos = Point(c, r);
break;
}
}
if (pos.x >= 0) break;
}
if (pos.x < 0) return;
// Put connected pieces
for (int c = 0; c < 4; ++c)
{
int next = positions(idx_piece, c);
if (next > 0)
{
switch (c)
{
case NORTH:
labels(Point(pos.x, pos.y - 1)) = next;
posed.insert(next);
break;
case SOUTH:
labels(Point(pos.x, pos.y + 1)) = next;
posed.insert(next);
break;
case WEST:
labels(Point(pos.x + 1, pos.y)) = next;
posed.insert(next);
break;
case EAST:
labels(Point(pos.x - 1, pos.y)) = next;
posed.insert(next);
break;
}
}
}
}
Mat3b buildPuzzle(const vector<Mat3b>& pieces, const Mat1i& positions, Size sz)
{
int n_pieces = pieces.size();
set<int> posed;
set<int> todo;
for (int i = 0; i < n_pieces; ++i) todo.insert(i);
Mat1i labels(n_pieces * 2 + 1, n_pieces * 2 + 1, -1);
// Place first element in the center
todo.erase(0);
labels(Point(n_pieces, n_pieces)) = 0;
posed.insert(0);
builfForPiece(0, posed, labels, positions);
// Build puzzle starting from the already placed elements
while (todo.size() > 0)
{
auto it = todo.begin();
int next = -1;
do
{
next = *it;
++it;
} while (posed.count(next) == 0 && it != todo.end());
todo.erase(next);
builfForPiece(next, posed, labels, positions);
}
// Posed all pieces, now collage!
vector<Point> pieces_position;
Mat1b mask = labels >= 0;
findNonZero(mask, pieces_position);
Rect roi = boundingRect(pieces_position);
Mat1i lbls = labels(roi);
Mat3b collage(roi.height * sz.height, roi.width * sz.width, Vec3b(0, 0, 0));
for (int r = 0; r < lbls.rows; ++r)
{
for (int c = 0; c < lbls.cols; ++c)
{
if (lbls(r, c) >= 0)
{
Rect rect(c*sz.width, r*sz.height, sz.width, sz.height);
pieces[lbls(r, c)].copyTo(collage(rect));
}
}
}
return collage;
}
int main()
{
// Load images
vector<String> filenames;
glob("D:\\SO\\img\\puzzle*", filenames);
vector<Mat3b> pieces(filenames.size());
for (int i = 0; i < filenames.size(); ++i)
{
pieces[i] = imread(filenames[i], IMREAD_COLOR);
}
// Find Relative positions
Mat1i positions;
findRelativePositions(pieces, positions);
// Build the puzzle
Mat3b puzzle = buildPuzzle(pieces, positions, pieces[0].size());
imshow("Puzzle", puzzle);
waitKey();
return 0;
}
NOTE
No, there is no built-in solution to perform this. Image stitching won't work since the images are not overlapped.
I cannot guarantee that this works for every puzzle, but should work for the most.
I probably should have worked this couple of hours, but it was fun :D
EDIT
Adding more puzzle pieces generates wrong results in the previous code version. This was due the (wrong) assumption that at most one piece is good enough to be connected with a given piece.
Now I added a cost matrix, and only the minimum cost piece is saved as neighbor of a given piece.
I added also a resolveConflicts function that avoid that one piece can be merged (in non-conflicting position) with more than one piece.
This is the result adding more pieces:
UPDATE
Considerations after increasing the number of puzzle pieces:
This solution it's dependent on the input order of pieces, since it turns out it has a greedy approach to find neighbors.
While searching for neighbors, it's better to compare the H channel in the HSV space. I updated the code above with this improvement.
The final solution needs probably some kind of global minimization of the of a global cost matrix. This will make the method independent on the input order. I'll be back on this asap.
Once you have loaded this images as OpenCV Mat, you can concatenate these Mat both vertically or horizontally using:
Mat A, B; // Images that will be concatenated
Mat H; // Here we will concatenate A and B horizontally
Mat V; // Here we will concatenate A and B vertically
hconcat(A, B, H);
vconcat(A, B, V);
If you need to concatenate more than two images, you can use these methods recursively.
By the way, I think these methods are not included in the OpenCV documentation, but I have used them in the past.
I have 5rows x 4cols cv::Mat:
int output_size[] = {5,4};
cv::Mat im1(2, output_size, CV_32FC1);
float* ptr = (float*)im1.data;
for (unsigned int r = 0; r < output_size[0]; r++) {
for (unsigned int c = 0; c < output_size[1]; c++) {
*ptr = (r*im1.size.p[1])+c;
std::cout << *ptr++ << ",";
}
std::cout << std::endl;
}
So that matrix looks like this:
[ 0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15,
16, 17, 18, 19]
Furthermore, I have another 3depth x 5rows x 4cols cv::Mat:
int output_size2[] = {3,5,4};
cv::Mat im2(3, output_size2, CV_32FC1);
im2 = 0;
Now I want to copy im1 into, let's say, the second layer of im2. I do the following:
cv::Range rngs[] = {cv::Range(1,2), cv::Range::all(), cv::Range::all()};
cv::Mat dst = im2(rngs);
im1.copyTo(dst);
This doesn't seem to work. im1.copyTo(dst) has no effect on im2 - all the second layer values remain zero after the operation. After some introspection, it seems like opencv is finding that since the size of dst is 1x5x4 and not 5x4, it reassigns dst.
What would be the right way to copy a rectangular matrix into one layer of a 3D matrix?
Ok this works:
void* ptr = im2.data + im2.step[0]*1;
memcpy(ptr, (void*)im1.data, im1.total()*sizeof(float));
but is there an "opencv" way to solve it.
If you have n 2-dim images and you want to use them as layers in a 3D matrix that has a third dimension of size n you can use cv::merge. see documentation.
See also:
cv:split documentation