I need to understand how clEnqueueCopyBufferRect is working.
For Example, I need to copy 3x3 region from 4x4 region (let it be the int array) with origin (1,1).
I have two suggestions:
It just copy the linear region which size matches the size of rectangular 3x3 region, so the result is:
1 1 1 1 0 0 0 0
1 1 1 1 -> 0 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 0 0
Or it copy the rectangular region and the result is:
1 1 1 1 0 0 0 0
1 1 1 1 -> 0 1 1 1
1 1 1 1 0 1 1 1
1 1 1 1 0 1 1 1
To check this I used next example code:
int main(int argc, char * argv[])
{
std::vector<Platform> platforms;
Platform::get(&platforms);
cl::Platform platform = platforms[0];
cl_context_properties cps[3] = {CL_CONTEXT_PLATFORM, (cl_context_properties)platform(), 0};
cl::Context context(CL_DEVICE_TYPE_GPU,cps);
std::string kr_str = "__kernel void StepKernel(__global int* in) { in[get_global_id(1)*4 + get_global_id(0)] = 1;}";
Program program=cl::Program(
context,
cl::Program::Sources(
1,
std::make_pair(kr_str.c_str(),kr_str.length()+1)
)
);
std::vector<cl::Device> devices = context.getInfo<CL_CONTEXT_DEVICES>();
std::vector<cl::Device> device(1,devices[0]);
program.build(device);
cl::Kernel kernel = cl::Kernel(program, "StepKernel");
cl::CommandQueue queue(
context,
device[0],
CL_NONE
);
cl::Buffer in_buffer_on_device(
context,
CL_MEM_READ_WRITE,
16*sizeof(int)
);
cl::Buffer out_buffer_on_host(
context,
CL_MEM_READ_WRITE|CL_MEM_ALLOC_HOST_PTR,
16*sizeof(int)
);
void *ptr_on_host =
queue.enqueueMapBuffer(
out_buffer_on_host,
CL_BLOCKING,
CL_MAP_WRITE|CL_MAP_READ,
0, 16*sizeof(int),
NULL, NULL
);
for(int k = 0; k < 4; k++)
for(int i = 0; i < 4; i++)
static_cast<int*>(ptr_on_host)[k*4 + i] = 0;
cl::size_t<3> origin, region;
origin[0]=0; origin[1]=0; origin[2]=0;
region[0]=4*sizeof(int); region[1]=4; region[2]=1;
Event evt;
queue.enqueueCopyBufferRect(
out_buffer_on_host,
in_buffer_on_device,
origin, origin, region,
sizeof(int)*4,sizeof(int)*4*4,sizeof(int)*4,sizeof(int)*4*4,
NULL,
&evt
);
evt.wait();
kernel.setArg(0,in_buffer_on_device);
queue.enqueueNDRangeKernel(
kernel,
cl::NDRange( 0, 0),
cl::NDRange( 4, 4),
cl::NullRange,
NULL,
&evt
);
evt.wait();
origin[0]=1*sizeof(int); origin[1]=1; origin[2]=0;
region[0]=3*sizeof(int); region[1]=3; region[2]=1;
queue.enqueueCopyBufferRect(
in_buffer_on_device,
out_buffer_on_host,
origin, origin, region,
sizeof(int)*4,sizeof(int)*4*4,sizeof(int)*4,sizeof(int)*4*4,
NULL,
&evt
);
evt.wait();
for(int k = 0; k < 4; k++)
{
for(int i = 0; i < 4; i++)
{
std::cout << static_cast<int*>(ptr_on_host)[k*4 + i]<< "\t";
}
std::cout << std::endl;
}
return 0;
}
And the output was:
0 0 0 0
0 1 1 1
1 1 1 1
1 1 0 0
So, if i have not done any mistake the CL copies linear region. So such behavior is useless for me.
It copies the rectangular region. Otherwise it would be quite useless as an API.
Related
There is a figure that is represented by 1 values that are “connected” vertically, horizontally or diagonally in a 2 dementional array.
I need to save the index of the boundary of the figure (the row and column of the 0's that are connected to the figure, in any type of c++ container.
For instance, in the following 2d array, I should get the following indexes:
(0,2), (0,3), (0,4), (1,2), (1,4), (1,5), (2,2), (2,3), (2,5), (2,6)... etc.
0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0
0 0 0 0 1 0 0 0
0 0 0 1 1 1 0 0
0 0 0 0 1 1 0 0
0 0 0 1 1 1 0 0
0 0 0 1 1 0 0 0
0 0 0 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
What is the most efficient way to do so, on both space and time complexity?
void dfs(vector<vector<int>>& matrix, vector<vector<int>>& boundary, int rows, int cols, int i, int j){
if(!isValidCoordinate(i, j))
return;
if(isAnyNeighborOne(i, j)){
boundary.push_back({i, j});
matrix[i][j] = 2;
}
else
matrix[i][j] = 3;
//Explore eight directions
/* I didn't bother about x = 0 and y = 0.
* You can, if you want.
* Doesn't make a difference though.
*/
for(int x = -1; x < 2; x++){
for(int y = -1; y < 2; y++){
dfs(matrix, boundary, rows, cols, i + x, i + y);
}
}
}
vector<vector<int>> getBoundary(vector<vector<int>>& matrix){
vector<vector<int>> boundary;
int rows = matrix.size();
if(!rows)
return boundary;
int cols = matrix[0].size();
for(int i = 0; i < rows; i++){
for(int j = 0; j < cols; j++){
if(matrix[i][j] == 0){
dfs(matrix, boundary, rows, cols, i, j);
}
}
}
return boundary;
}
If you print the matrix at the end, you'll see the boundary with 2.
Whatever you see as 3, if you want, you can set it back to 0.
isValidCoordinate() and isAnyNeighborOne() is left to you as an exercise.
I use vector<vector<int>> for boundary. You can try using vector<pair<int,int>> as well.
With the above solution you'll get inner boundary as well as outer boundary. As an exercise, you can try only inner boundary or only outer boundary.
You can solve the same problem with BFS as well. If the matrix is of large size, stack might overflow due to recursive calls. Better to prefer BFS in such cases.
Time and space complexity of the above solution is O(rows * cols).
Yes. I want to get the same result like the imrotate in matlab, but I can't get the point.
More specifically,I get a mat A
a =
1 2 3
4 5 6
imrotate(a, 45)
ans =
0 0 0 0 0
0 0 3 6 0
0 1 2 6 0
0 1 4 0 0
0 0 0 0 0
How can I do that using OpenCV?
You may need use getRotationMatrix2D and warpAffine.
like this:
Mat A(5, 5, CV_8U, Scalar(0));
Point center(3, 3);
double angle = 45;
int value=1;
for (int i = 2 ; i<4 ;i++)
{
for (int j = 1 ; j <4 ;j++)
{
A.at(j,i) = value;
value++;
}
}
Mat matrix = getRotationMatrix2D(center, angle, 1.0);
Mat imgRotation;
warpAffine(A, imgRotation, matrix, Size(5, 5));
I am trying to find a solution of the puzzle game 'Flood It'. The main idea is to turn a whole N*M game board of k different colors into a single color. I have to start from the top left corner of the board and turn the same colored block into one of the colors of neighboring nodes and thus moving ahead and flooding the whole board into a single color at last. For example:
Initial Board:
1 1 1 2 2 3
1 1 2 3 4 5
1 1 1 1 3 4
1 4 3 2 1 5
2 3 4 5 1 2
Final Board:
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
where 1,2,3,4,5 represents different colors. I have prepared a C++ code for finding out the area of same colored block at any position of the board . This can be applied at the top left cell at first and then at the neighboring nodes of it to flood the color. My code is as follows:
#include <cstdint>
#include <vector>
#include <queue>
#include <string>
#include <iostream>
typedef std::vector<int32_t> vec_1d;
typedef std::vector<vec_1d> vec_2d;
// Print the 2d vector with a label
void dump(std::string const& label, vec_2d const& v)
{
std::cout << label << "\n";
for (std::size_t y(0); y < v.size(); ++y) {
for (std::size_t x(0); x < v[0].size(); ++x) {
std::cout << v[y][x] << " ";
}
std::cout << "\n";
}
std::cout << "\n";
}
// Recursive implementation of the search
void find_connected_r(int32_t target_color
, std::size_t x
, std::size_t y
, vec_2d const& colors
, vec_2d& result)
{
if ((result[y][x] == 1) || (colors[y][x] != target_color)) {
return;
}
result[y][x] = 1;
std::size_t width(colors[0].size());
std::size_t height(colors.size());
if (x > 0) {
find_connected_r(target_color, x - 1, y, colors, result);
}
if (y > 0) {
find_connected_r(target_color, x, y - 1, colors, result);
}
if (x < (width - 1)) {
find_connected_r(target_color, x + 1, y, colors, result);
}
if (y < (height - 1)) {
find_connected_r(target_color, x, y + 1, colors, result);
}
}
// Entry point to the search, select the implementation with last param
vec_2d find_connected(std::size_t x, std::size_t y, vec_2d const& colors, bool recursive)
{
if (colors.empty() || colors[0].empty()) {
throw std::runtime_error("Invalid input array size");
}
int32_t target_color(colors[y][x]);
vec_2d result(colors.size(), vec_1d(colors[0].size(), 0));
if (recursive) {
find_connected_r(target_color, x, y, colors, result);
}
else {
find_connected(target_color, x, y, colors, result);
}
return result;
}
void dump_coordinates(std::string const& label, vec_2d const& v)
{
std::cout << label << "\n";
for (std::size_t y(0); y < v.size(); ++y) {
for (std::size_t x(0); x < v[0].size(); ++x) {
if (v[y][x]) {
std::cout << "(" << x << ", " << y << ") ";
}
}
}
std::cout << "\n";
}
int main()
{
vec_2d colors{
{ 1, 1, 1, 1, 1, 1 }
, { 2, 2, 2, 3, 3, 1 }
, { 1, 1, 1, 1, 3, 1 }
, { 1, 3, 3, 3, 3, 1 }
, { 1, 1, 1, 1, 1, 1 }
};
}
How will I turn the whole board/matrix into a single color by examining the neighboring nodes?
A possible top-level algorithm to solve this puzzle is to repeat the following until there is only one color on the whole board:
Find all contiguous color regions. Treat the region at (0,0) as primary, all others as secondary.
Pick the largest (by count of tiles) secondary region with a color that is different to the primary region's color. Let's name the color of this secondary region the new_color.
Recolor the primary region to new_color.
Finding all the regions
We should keep a cumulative_mask to track of all the tiles that are already identified as part of some region.
First we find the primary region, starting search at (0,0), and update our cumulative_mask with the result.
Then repeat until no more regions can be found:
Find the position of the first zero tile in the cumulative_mask, which has at least one non-zero tile in the primary region mask.
Find the region starting at this position.
Update the cumulative_mask with the mask of this region.
Selecting the color
Simply iterate through secondary regions, and find the region with largest count, which has a different color than the primary region.
Code
(also on coliru)
Note: Intentionally written in a way to make it possible to understand the algorithm. This could definitely be refactored, and it's missing a lot of error checking.
#include <cstdint>
#include <vector>
#include <queue>
#include <string>
#include <iostream>
typedef std::vector<int32_t> vec_1d;
typedef std::vector<vec_1d> vec_2d;
typedef std::pair<std::size_t, std::size_t> position;
position const INVALID_POSITION(-1, -1);
int32_t const INVALID_COLOR(0);
// ============================================================================
struct region_info
{
int32_t color;
vec_2d mask;
std::size_t count() const
{
std::size_t result(0);
for (std::size_t y(0); y < mask.size(); ++y) {
for (std::size_t x(0); x < mask[0].size(); ++x) {
if (mask[y][x]) {
++result;
}
}
}
return result;
}
};
struct region_set
{
// The region that contains (0, 0)
region_info primary;
// All other regions
std::vector<region_info> secondary;
};
// ============================================================================
// Print the 2D vector with a label
void dump(std::string const& label, vec_2d const& v)
{
std::cout << label << "\n";
for (std::size_t y(0); y < v.size(); ++y) {
for (std::size_t x(0); x < v[0].size(); ++x) {
std::cout << v[y][x] << " ";
}
std::cout << "\n";
}
std::cout << "\n";
}
// Print the coordinates of non-zero elements of 2D vector with a label
void dump_coordinates(std::string const& label, vec_2d const& v)
{
std::cout << label << "\n";
for (std::size_t y(0); y < v.size(); ++y) {
for (std::size_t x(0); x < v[0].size(); ++x) {
if (v[y][x]) {
std::cout << "(" << x << ", " << y << ") ";
}
}
}
std::cout << "\n";
}
void dump(region_info const& ri)
{
std::cout << "Region color: " << ri.color << "\n";
std::cout << "Region count: " << ri.count() << "\n";
dump("Region mask:", ri.mask);
}
void dump(region_set const& rs)
{
std::cout << "Primary Region\n" << "\n";
dump(rs.primary);
for (std::size_t i(0); i < rs.secondary.size(); ++i) {
std::cout << "Secondary Region #" << i << "\n";
dump(rs.secondary[i]);
}
}
// ============================================================================
// Find connected tiles - implementation
void find_connected(int32_t target_color
, std::size_t x
, std::size_t y
, vec_2d const& colors
, vec_2d& result)
{
std::size_t width(colors[0].size());
std::size_t height(colors.size());
std::queue<position> s;
s.push(position(x, y));
while (!s.empty()) {
position pos(s.front());
s.pop();
if (result[pos.second][pos.first] == 1) {
continue;
}
if (colors[pos.second][pos.first] != target_color) {
continue;
}
result[pos.second][pos.first] = 1;
if (pos.first > 0) {
s.push(position(pos.first - 1, pos.second));
}
if (pos.second > 0) {
s.push(position(pos.first, pos.second - 1));
}
if (pos.first < (width - 1)) {
s.push(position(pos.first + 1, pos.second));
}
if (pos.second < (height - 1)) {
s.push(position(pos.first, pos.second + 1));
}
}
}
// Find connected tiles - convenience wrapper
vec_2d find_connected(std::size_t x, std::size_t y, vec_2d const& colors)
{
if (colors.empty() || colors[0].empty()) {
throw std::runtime_error("Invalid input array size");
}
int32_t target_color(colors[y][x]);
vec_2d result(colors.size(), vec_1d(colors[0].size(), 0));
find_connected(target_color, x, y, colors, result);
return result;
}
// ============================================================================
// Change color of elements at positions with non-zero mask value to new color
vec_2d& change_masked(int32_t new_color
, vec_2d& colors
, vec_2d const& mask)
{
for (std::size_t y(0); y < mask.size(); ++y) {
for (std::size_t x(0); x < mask[0].size(); ++x) {
if (mask[y][x]) {
colors[y][x] = new_color;
}
}
}
return colors;
}
// Combine two masks
vec_2d combine(vec_2d const& v1, vec_2d const& v2)
{
vec_2d result(v1);
for (std::size_t y(0); y < v2.size(); ++y) {
for (std::size_t x(0); x < v2[0].size(); ++x) {
if (v2[y][x]) {
result[y][x] = v2[y][x];
}
}
}
return result;
}
// Find position of first zero element in mask
position find_first_zero(vec_2d const& mask)
{
for (std::size_t y(0); y < mask.size(); ++y) {
for (std::size_t x(0); x < mask[0].size(); ++x) {
if (!mask[y][x]) {
return position(x, y);
}
}
}
return INVALID_POSITION;
}
bool has_nonzero_neighbor(std::size_t x, std::size_t y, vec_2d const& mask)
{
bool result(false);
if (x > 0) {
result |= (mask[y][x - 1] != 0);
}
if (y > 0) {
result |= (mask[y - 1][x] != 0);
}
if (x < (mask[0].size() - 1)) {
result |= (mask[y][x + 1] != 0);
}
if (y < (mask.size() - 1)) {
result |= (mask[y + 1][x] != 0);
}
return result;
}
// Find position of first zero element in mask
// which neighbors at least one non-zero element in primary mask
position find_first_zero_neighbor(vec_2d const& mask, vec_2d const& primary_mask)
{
for (std::size_t y(0); y < mask.size(); ++y) {
for (std::size_t x(0); x < mask[0].size(); ++x) {
if (!mask[y][x]) {
if (has_nonzero_neighbor(x, y, primary_mask)) {
return position(x, y);
}
}
}
}
return INVALID_POSITION;
}
// ============================================================================
// Find all contiguous color regions in the image
// The region starting at (0,0) is considered the primary region
// All other regions are secondary
// If parameter 'only_neighbors' is true, search only for regions
// adjacent to primary region, otherwise search the entire board
region_set find_all_regions(vec_2d const& colors, bool only_neighbors = false)
{
region_set result;
result.primary.color = colors[0][0];
result.primary.mask = find_connected(0, 0, colors);
vec_2d cumulative_mask = result.primary.mask;
for (;;) {
position pos;
if (only_neighbors) {
pos = find_first_zero_neighbor(cumulative_mask, result.primary.mask);
} else {
pos = find_first_zero(cumulative_mask);
}
if (pos == INVALID_POSITION) {
break; // No unsearched tiles left
}
region_info reg;
reg.color = colors[pos.second][pos.first];
reg.mask = find_connected(pos.first, pos.second, colors);
cumulative_mask = combine(cumulative_mask, reg.mask);
result.secondary.push_back(reg);
}
return result;
}
// ============================================================================
// Select the color to recolor the primary region with
// based on the color of the largest secondary region of non-primary color
int32_t select_color(region_set const& rs)
{
int32_t selected_color(INVALID_COLOR);
std::size_t selected_count(0);
for (auto const& ri : rs.secondary) {
if (ri.color != rs.primary.color) {
if (ri.count() > selected_count) {
selected_count = ri.count();
selected_color = ri.color;
}
}
}
return selected_color;
}
// ============================================================================
// Solve the puzzle
// If parameter 'only_neighbors' is true, search only for regions
// adjacent to primary region, otherwise search the entire board
// Returns the list of selected colors representing the solution steps
vec_1d solve(vec_2d colors, bool only_neighbors = false)
{
vec_1d selected_colors;
for (int32_t i(0);; ++i) {
std::cout << "Step #" << i << "\n";
dump("Game board: ", colors);
region_set rs(find_all_regions(colors, true));
dump(rs);
int32_t new_color(select_color(rs));
if (new_color == INVALID_COLOR) {
break;
}
std::cout << "Selected color: " << new_color << "\n";
selected_colors.push_back(new_color);
change_masked(new_color, colors, rs.primary.mask);
std::cout << "\n------------------------------------\n\n";
}
return selected_colors;
}
// ============================================================================
int main()
{
vec_2d colors{
{ 1, 1, 1, 1, 1, 1 }
, { 2, 2, 2, 3, 3, 1 }
, { 1, 1, 4, 5, 3, 1 }
, { 1, 3, 3, 4, 3, 1 }
, { 1, 1, 1, 1, 1, 1 }
};
vec_1d steps(solve(colors, true));
std::cout << "Solved in " << steps.size() << " step(s):\n";
for (auto step : steps) {
std::cout << step << " ";
}
std::cout << "\n\n";
}
// ============================================================================
Output of the program:
Step #0
Game board:
1 1 1 1 1 1
2 2 2 3 3 1
1 1 4 5 3 1
1 3 3 4 3 1
1 1 1 1 1 1
Primary Region
Region color: 1
Region count: 18
Region mask:
1 1 1 1 1 1
0 0 0 0 0 1
1 1 0 0 0 1
1 0 0 0 0 1
1 1 1 1 1 1
Secondary Region #0
Region color: 2
Region count: 3
Region mask:
0 0 0 0 0 0
1 1 1 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
Secondary Region #1
Region color: 3
Region count: 4
Region mask:
0 0 0 0 0 0
0 0 0 1 1 0
0 0 0 0 1 0
0 0 0 0 1 0
0 0 0 0 0 0
Secondary Region #2
Region color: 4
Region count: 1
Region mask:
0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
Secondary Region #3
Region color: 3
Region count: 2
Region mask:
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 1 1 0 0 0
0 0 0 0 0 0
Secondary Region #4
Region color: 4
Region count: 1
Region mask:
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 1 0 0
0 0 0 0 0 0
Selected color: 3
------------------------------------
Step #1
Game board:
3 3 3 3 3 3
2 2 2 3 3 3
3 3 4 5 3 3
3 3 3 4 3 3
3 3 3 3 3 3
Primary Region
Region color: 3
Region count: 24
Region mask:
1 1 1 1 1 1
0 0 0 1 1 1
1 1 0 0 1 1
1 1 1 0 1 1
1 1 1 1 1 1
Secondary Region #0
Region color: 2
Region count: 3
Region mask:
0 0 0 0 0 0
1 1 1 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
Secondary Region #1
Region color: 4
Region count: 1
Region mask:
0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
Secondary Region #2
Region color: 5
Region count: 1
Region mask:
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 1 0 0
0 0 0 0 0 0
0 0 0 0 0 0
Secondary Region #3
Region color: 4
Region count: 1
Region mask:
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 1 0 0
0 0 0 0 0 0
Selected color: 2
------------------------------------
Step #2
Game board:
2 2 2 2 2 2
2 2 2 2 2 2
2 2 4 5 2 2
2 2 2 4 2 2
2 2 2 2 2 2
Primary Region
Region color: 2
Region count: 27
Region mask:
1 1 1 1 1 1
1 1 1 1 1 1
1 1 0 0 1 1
1 1 1 0 1 1
1 1 1 1 1 1
Secondary Region #0
Region color: 4
Region count: 1
Region mask:
0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
Secondary Region #1
Region color: 5
Region count: 1
Region mask:
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 1 0 0
0 0 0 0 0 0
0 0 0 0 0 0
Secondary Region #2
Region color: 4
Region count: 1
Region mask:
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 1 0 0
0 0 0 0 0 0
Selected color: 4
------------------------------------
Step #3
Game board:
4 4 4 4 4 4
4 4 4 4 4 4
4 4 4 5 4 4
4 4 4 4 4 4
4 4 4 4 4 4
Primary Region
Region color: 4
Region count: 29
Region mask:
1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 0 1 1
1 1 1 1 1 1
1 1 1 1 1 1
Secondary Region #0
Region color: 5
Region count: 1
Region mask:
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 1 0 0
0 0 0 0 0 0
0 0 0 0 0 0
Selected color: 5
------------------------------------
Step #4
Game board:
5 5 5 5 5 5
5 5 5 5 5 5
5 5 5 5 5 5
5 5 5 5 5 5
5 5 5 5 5 5
Primary Region
Region color: 5
Region count: 30
Region mask:
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
Solved in 4 step(s):
3 2 4 5
There's a bunch of things I don't understand in your code so instead of trying to fix them I'll create a new function and you can compare the two.
// this function is called when the user inputs the x and y values
// the colors vector will be modified in place by reference
void change_color(int x, int y, vec_2d& colors)
{
int target_color = colors[x][y];
// call the recursive flood fill function
flood_fill(0, 0, target_color, colors);
}
//this function is the recursive flood fill
void flood_fill(int x, int y, const int target_color, vec_2d& colors)
{
// if the current tile is already the target color, do nothing
if (colors[x][y] == target_color) return;
// only need to go right and down, since starting from top left
// Also, only goes to the next tile if the next tile's color is
// the same as the current tile's color
if (x < colors.size()-1 && colors[x+1][y] == colors[x][y])
{
flood_fill(x+1, y, target_color, colors);
}
if (y < colors[0].size()-1 && colors[x][y+1] == colors[x][y])
{
flood_fill(x, y+1, target_color, colors);
}
// finally, fill in the current tile with target_color
colors[x][y] = target_color;
}
EDIT: Since you meant you wanted to solve the game instead of implementing the game...
Keep track of which colors are still available on the board at all times. On each "turn", find the color that will fill the most tile area starting from the top left. Repeat until all tiles are filled with the same color.
This is more of a brute force approach, and there is probably a more optimized method, but this is the most basic one in my opinion.
I have an exercise for university where I have to write a DFS algorithm to run on an undirected graph. I also have to make the program sum the values of all nodes show the order in which the nodes were visited.
Here is the given structure:
#include <iostream>
#include <cassert>
using namespace std;
struct node {
// DATA STRUCTURE NODES
};
int dfs_sum(/* FUNCTION ARGUMENTS */) {
// DEPTH FIRST SEARCH ALGORITHM
}
void node_init(/* FUNCTION ARGUMENTS */) {
// INITIALIZATION OF NODE WITH LABEL "value" AND NEIGHBOR "num_adjacent"
}
void edge_init(/* FUNCTION ARGUMENTS */) {
// INITIALIZATION OF EDGE BETWEEN TWO NODES
}
void node_delete(/* FUNCTION ARGUMENTS */) {
// DE-ALLOCATE MEMORY THAT WAS ALLOCATED IN "node_init"
}
void init_nodes(node *nodes) {
node_init(&nodes[0], 1, 1);
node_init(&nodes[1], 2, 4);
node_init(&nodes[2], 3, 1);
node_init(&nodes[3], 4, 4);
node_init(&nodes[4], 5, 4);
node_init(&nodes[5], 6, 2);
node_init(&nodes[6], 7, 5);
node_init(&nodes[7], 8, 3);
node_init(&nodes[8], 9, 2);
node_init(&nodes[9], 10, 2);
node_init(&nodes[10], 11, 4);
node_init(&nodes[11], 12, 2);
edge_init(&nodes[0], &nodes[1]);
edge_init(&nodes[1], &nodes[4]);
edge_init(&nodes[1], &nodes[6]);
edge_init(&nodes[1], &nodes[7]);
edge_init(&nodes[2], &nodes[3]);
edge_init(&nodes[3], &nodes[6]);
edge_init(&nodes[3], &nodes[7]);
edge_init(&nodes[3], &nodes[11]);
edge_init(&nodes[4], &nodes[5]);
edge_init(&nodes[4], &nodes[8]);
edge_init(&nodes[4], &nodes[9]);
edge_init(&nodes[5], &nodes[6]);
edge_init(&nodes[6], &nodes[9]);
edge_init(&nodes[6], &nodes[10]);
edge_init(&nodes[7], &nodes[10]);
edge_init(&nodes[8], &nodes[10]);
edge_init(&nodes[10], &nodes[11]);
}
void delete_nodes(node *nodes) {
for (int i = 0; i < 12; ++i) {
node_delete(&nodes[i]);
}
}
int main() {
node *nodes= new node[12];
init_nodes(nodes);
int sum_dfs = dfs_sum(&nodes[0]);
cout << endl;
int sum_loop = 0;
for (int i = 0; i < 12; ++i) {
sum_loop += nodes[i].value;
}
cout << "sum_dfs = " << sum_dfs << " sum_loop = " << sum_loop << endl;
delete_nodes(nodes);
delete [] nodes;
return 0;
}
I do not know how to begin this exercise
I am not familiar with c++ (I think that's what you used) but the implementation is the same anyway so I can give you a pseudo-code of what the algorithm should look like.
create a stack where object will be stored
all nodes are not visited when we begin
push source in the stack and mark it as visited
while the stack is not empty;
go to the first adjacent node to source and if it has not been visited
mark as visited and move to its next unvisited node and so on
if at any point you reach a node that cannot visited any other unvisited node
pop the stack until you can visited an unvisited node.
Do this until the stack is empty
Below is a simple implementation using an adjacency matrix
void dfs(int adjacency_matrix[][], int source){
Stack<Integer> stack = new Stack<>();
int numNodes = adjacency_matrix[source].length -1;
boolean [] visited = new boolean[numNodes +1];
visited[source] = true;
stack.add(source);
while(!stack.isEmpty()){
int current = stack.peek(); // don't remove the element but get it
System.out.println("Current node being visited is "+current);
for(int x = 0; x <= numNodes; x++){
if(adjacency_matrix[current][x] == 1 && visited[x] == false){
visited[x] = true;
stack.push(x);
break;
}else if(x == numNodes){
stack.pop();
}
}
}
}
You can test with a graph like this
0 --- 1-------5----6--8
| \ \ | / /
| \ \ | / /
| \ \ | / /
2 3----4---7---9
0 1 2 3 4 5 6 7 8 9
---------------------
0 | 0 1 1 1 0 0 0 0 0 0
1 | 1 0 0 0 1 1 0 0 0 0
2 | 1 0 0 0 0 0 0 0 0 0
3 | 1 0 0 0 1 0 0 0 0 0
4 | 0 1 0 1 0 0 0 1 0 0
5 | 0 1 0 0 0 0 1 1 0 0
6 | 0 0 0 0 0 1 0 1 1 0
7 | 0 0 0 0 1 1 1 0 0 1
8 | 0 0 0 0 0 0 1 0 0 1
9 | 0 0 0 0 0 0 0 1 1 0
---------------------
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I'm looking to turn a 1-bit bmp file of variable height/width into a simple two-dimensional array with values of either 0 or 1. I don't have any experience with image editing in code and most libraries that I've found involve higher bit-depth than what I need. Any help regarding this would be great.
Here's the code to read a monochrome .bmp file
(See dmb's answer below for a small fix for odd-sized .bmps)
#include <stdio.h>
#include <string.h>
#include <malloc.h>
unsigned char *read_bmp(char *fname,int* _w, int* _h)
{
unsigned char head[54];
FILE *f = fopen(fname,"rb");
// BMP header is 54 bytes
fread(head, 1, 54, f);
int w = head[18] + ( ((int)head[19]) << 8) + ( ((int)head[20]) << 16) + ( ((int)head[21]) << 24);
int h = head[22] + ( ((int)head[23]) << 8) + ( ((int)head[24]) << 16) + ( ((int)head[25]) << 24);
// lines are aligned on 4-byte boundary
int lineSize = (w / 8 + (w / 8) % 4);
int fileSize = lineSize * h;
unsigned char *img = malloc(w * h), *data = malloc(fileSize);
// skip the header
fseek(f,54,SEEK_SET);
// skip palette - two rgb quads, 8 bytes
fseek(f, 8, SEEK_CUR);
// read data
fread(data,1,fileSize,f);
// decode bits
int i, j, k, rev_j;
for(j = 0, rev_j = h - 1; j < h ; j++, rev_j--) {
for(i = 0 ; i < w / 8; i++) {
int fpos = j * lineSize + i, pos = rev_j * w + i * 8;
for(k = 0 ; k < 8 ; k++)
img[pos + (7 - k)] = (data[fpos] >> k ) & 1;
}
}
free(data);
*_w = w; *_h = h;
return img;
}
int main()
{
int w, h, i, j;
unsigned char* img = read_bmp("test1.bmp", &w, &h);
for(j = 0 ; j < h ; j++)
{
for(i = 0 ; i < w ; i++)
printf("%c ", img[j * w + i] ? '0' : '1' );
printf("\n");
}
return 0;
}
It is plain C, so no pointer casting - beware while using it in C++.
The biggest problem is that the lines in .bmp files are 4-byte aligned which matters a lot with single-bit images. So we calculate the line size as "width / 8 + (width / 8) % 4". Each byte contains 8 pixels, not one, so we use the k-based loop.
I hope the other code is obvious - much has been told about .bmp header and pallete data (8 bytes which we skip).
Expected output:
0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0
0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0
0 0 0 0 0 0 1 1 1 1 0 0 1 1 0 0
0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 1 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 0 0 1 0 0 0
0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1 1 0 0 0 0 1 0
0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0
0 0 0 1 0 1 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0
I tried the solution of Viktor Lapyov on a 20x20 test image:
But with his code, I get this output (slightly reformatted but you can see the problem):
The last 4 pixels are not read. The problem is here. (The last partial byte in a row is ignored.)
// decode bits
int i, j, k, rev_j;
for(j = 0, rev_j = h - 1; j < h ; j++, rev_j--) {
for(i = 0 ; i < w / 8; i++) {
int fpos = j * lineSize + i, pos = rev_j * w + i * 8;
for(k = 0 ; k < 8 ; k++)
img[pos + (7 - k)] = (data[fpos] >> k ) & 1;
}
}
I rewrote the inner loop like this:
// decode bits
int i, byte_ctr, j, rev_j;
for(j = 0, rev_j = h - 1; j < h ; j++, rev_j--) {
for( i = 0; i < w; i++) {
byte_ctr = i / 8;
unsigned char data_byte = data[j * lineSize + byte_ctr];
int pos = rev_j * w + i;
unsigned char mask = 0x80 >> i % 8;
img[pos] = (data_byte & mask ) ? 1 : 0;
}
}
and all is well:
The following c code works with monochrome bitmaps of any size. I'll assume you've got your bitmap in a buffer with heights and width initialized from file. So
// allocate mem for global buffer
if (!(img = malloc(h * w)) )
return(0);
int i = 0, k, j, scanline;
// calc the scanline. Monochrome images are
// padded with 0 at every line end. This
// makes them divisible by 4.
scanline = ( w + (w % 8) ) >> 3;
// account for the paddings
if (scanline % 4)
scanline += (4 - scanline % 4);
// loop and set the img values
for (i = 0, k = h - 1; i < h; i++)
for (j = 0; j < w; j++) {
img[j+i*w] = (buffer[(j>>3)+k*scanline])
& (0x80 >> (j % 8));
}
Hope this help's. To convert it to 2D is now a trivial matter: But if u get lost here is the math to convert 1D array to 2D suppose r & c are row and column and w is the width then:
. c + r * w = r, c
If you got further remarks hit me back, am out!!!
Lets think of a1x7 monochrome bitmap i.e. This is a bitmap of a straight line with 7 pixels wide. To store this image on a Windows OS; since 7 is not evenly divisible by 4 it's going to pad in it an extra 3 bytes.
So the biSizeImage of the BITMAPINFOHEADER structure will show a total of 4 bytes. Nonetheless the biHeight and biWidth members will correctly state the true bitmap dimensions.
The above code will fail because 7 / 8 = 0 (by rounding off as with all c compilers do). Hence loop "i" will not execute so will "k".
That means the vector "img" now contains garbage values that do not correspond to the pixels contained in " data" i.e. the result is incorrect.
And by inductive reasoning if it does not satisfy the base case then chances are it wont do much good for general cases.