Opencv应用——水排序谜题

目录

一,背景

二,准备工作

三,图片分析

1,颜色分析

2,颜色对应

3,定位

四,完整代码


一,背景

初学opencv,刚好最近在玩水排序谜题水排序谜题_csuzhucong的博客-CSDN博客

我已经做出一个程序,输入颜色就可以给出答案。

这几天在学opencv,一下子就想到,用程序直接读颜色就不用手动输入了,而且这个应用比较简单,用来自学练手也很合适。

二,准备工作

1,opencv入门

Opencv 图片处理_mat 坐标_csuzhucong的博客-CSDN博客

2,给游戏换个最朴素的背景,降低处理难度。

三,图片分析

1,颜色分析

首先看下图片颜色够不够纯,用单通道能不能很好的区分。

我们可以用map把像素值计数,然后把计数较高的打印出来看看:

    map<int, int>m;
	for (int i = 0; i < image.rows; i++) {
		for (int j = 0; j < image.cols; j++) {
			m[int(image.at<uchar>(i, j))]++;
		}
	}
	for (auto i : m) {
		if(i.second>30000)cout << i.first << " " << i.second << endl;
	}

结果:

94 40562
96 34847
121 37256
126 37201
127 38850
133 35969
134 41809
142 35440
166 39223
167 38344
177 36104
184 365437
192 127481
199 38878
205 39044
231 38935
242 1074800

结果表明,颜色非常纯,所以处理起来会非常简单。

2,颜色对应

把某个颜色的色块换成白色,以94为例

	for (int i = 0; i < image.rows; i++) {
		for (int j = 0; j < image.cols; j++) {
			if (int(image.at<uchar>(i, j)) == 94)image.at<uchar>(i, j) = 255;
			//else image.at<uchar>(i, j) = 0;
		}
	}
	Size dsize = Size(round(0.3 * image.cols), round(0.3 * image.rows));
	Mat shrink;
	resize(image, shrink, dsize, 0, 0, INTER_AREA);
	imshow("shrink", shrink);
	waitKey(0);

输出:

所以94是褐色

依次类推得到所有颜色的像素值:231 黄色  121 绿色  199 翠绿 177 青色  142 蓝色  134 紫色 127 棕色 166 橙色126 红色 167 灰蓝 94 褐色  205 粉色

最亮的242显然是背景色,即下图的白色:

完整代码:

void readImage()
{
	string path = "D:\im.jpg";
	Mat image = imread(path, IMREAD_GRAYSCALE);
	if (!image.data) {
		cout << "imread fail\n";
		return;
	}
	cout << "rows=" << image.rows << endl;
	cout << "cols=" << image.cols << endl;
	cout << "channels=" << image.channels() << endl;
	map<int, int>m;
	for (int i = 0; i < image.rows; i++) {
		for (int j = 0; j < image.cols; j++) {
			m[int(image.at<uchar>(i, j))]++;
		}
	}
	for (auto i : m) {
		if (i.second > 30000)cout << i.first << " " << i.second << endl;
	}
	for (int i = 0; i < image.rows; i++) {
		for (int j = 0; j < image.cols; j++) {
			if (int(image.at<uchar>(i, j)) == 242)image.at<uchar>(i, j) = 255;
			else image.at<uchar>(i, j) = 0;
		}
	}
	Size dsize = Size(round(0.2 * image.cols), round(0.2 * image.rows));
	Mat shrink;
	resize(image, shrink, dsize, 0, 0, INTER_AREA);
	imshow("shrink", shrink);
	waitKey(0);
}

3,定位

因为图片是固定的,所以不需要自己实现,直接用画图软件看一下就行了。

我们用每个色块的中心位置的像素来代表整个色块。

单个色块的高度是90,所以8个纵坐标分别是770,860,950,1040,1340,1430,1520,1610

色块的横坐标之差大概是150,所以7个横坐标分别是90,240,390,540,690,840,990

这样,56个色块的坐标就全部出来了。

四,完整代码

v1.0代码

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<map>
#include<opencv2/opencv.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/core/mat.hpp>
using namespace std;
using namespace cv;

#pragma comment(lib,"../x64/vc14/lib/opencv_world452d.lib")
#pragma comment(lib,"../x64/vc14/lib/opencv_world452.lib")

#define NC 12
#define SIZE 4
vector<vector<int>>vm(14); //试管
map<vector<vector<int>>,int>visit;
const int x[8] = { 770,860,950,1040,1340,1430,1520,1610 };
const int y[7] = { 90,240,390,540,690,840,990 };
const int color[NC] = { 231,121,199,177,142,134,127,166,126,167,94,205 };//12个颜色顺序无所谓

void Init()
{
	string path = "D:/im.jpg";
	Mat image = imread(path, IMREAD_GRAYSCALE);
	if (!image.data) {
		cout << "imread fail\n";
		return;
	}
	map<int, int>m;
	for (int i = 0; i < NC; i++)m[color[i]] = i + 1;//反射
	for (int i = 0; i < 8; i++) {
		for (int j = 0; j < 7; j++) {
			int vmi = j + (i >= 4) * 7;
			int c = int(image.at<uchar>(x[i], y[j]));
			if(m[c])vm[vmi].insert(vm[vmi].begin(), m[c] - 1);
		}
	}
}

bool oneCol(vector<int>&vi) //是否纯色
{
	for (int i = 1; i < vi.size(); i++)if (vi[i] != vi[i - 1])return false;
	return true;
}

bool end()
{
	for (auto &vi : vm) if (!oneCol(vi))return false;
	return true;
}

bool canPour(int i, int j) //i能否倒给j
{
	if (i == j)return false;
	int si = vm[i].size(), sj = vm[j].size();
	if (si == 0 || sj == SIZE)return false;
	if (sj == 0) { // 排除纯色元素倒入空试管的情况
		return !oneCol(vm[i]);
	}
	int ci = vm[i][si - 1], cj = vm[j][sj - 1];
	if (ci != cj)return false;
	int num = 0;
	for (int k = si - 1; k >= 0; k--)if (vm[i][k] == ci)num++;
	return sj + num <= SIZE; // 加了同色必须倒完的限制,提高搜索效率
}

int pour(int i, int j) //返回倒了几个
{
	int x = 0;
	while (canPour(i, j)) {
		auto it = vm[i].end() - 1;
		vm[j].emplace_back(*it);
		vm[i].erase(it);
		x++;
	}
	return x;
}

void pour_f(int i, int j, int num) //按照数目回溯
{
	while (num--) {
		auto it = vm[i].end() - 1;
		vm[j].emplace_back(*it);
		vm[i].erase(it);
	}
}

bool dfs(int deep)
{
	if (visit.find(vm) != visit.end())return false;
	visit[vm] = 1;
	if (end() || deep>50) {
		return true;
	}
	for (int i = 0; i < vm.size(); i++) {
		for (int j = 0; j < vm.size(); j++) {
			if (!canPour(i, j))continue;
			if (i == 8 && j == 3 && deep==3) {
				cout << 123;
			}
			int x = pour(i, j);
			if (dfs(deep+1)) {
				cout << "\ndeep = "<<deep<<" from " << i << " to " << j;
				return true;
			}
			pour_f(j, i, x);
		}
	}
	return false;
}

void outVm()
{
	cout << endl;
	for (auto vi : vm) {
		int si = vi.size();
		for (int i = SIZE - 1; i >= 0; i--) {
			if (i >= si)cout << "-1 ";
			else cout << vi[i] << " ";
		}
	}
}

int main()
{
	Init();
	dfs(0);
	outVm();
	return 0;
}

实战:

以第5.1关卡为例,截图,放到d盘根目录,重命名为im.jpg

运行程序,直接得到:

deep = 41 from 12 to 3
deep = 40 from 3 to 9
deep = 39 from 3 to 7
deep = 38 from 7 to 10
deep = 37 from 7 to 11
deep = 36 from 3 to 5
deep = 35 from 3 to 11
deep = 34 from 11 to 13
deep = 33 from 11 to 4
deep = 32 from 11 to 9
deep = 31 from 9 to 0
deep = 30 from 9 to 10
deep = 29 from 10 to 4
deep = 28 from 4 to 13
deep = 27 from 13 to 6
deep = 26 from 6 to 2
deep = 25 from 2 to 4
deep = 24 from 4 to 5
deep = 23 from 5 to 8
deep = 22 from 5 to 11
deep = 21 from 11 to 2
deep = 20 from 2 to 4
deep = 19 from 4 to 12
deep = 18 from 12 to 7
deep = 17 from 12 to 13
deep = 16 from 5 to 8
deep = 15 from 4 to 10
deep = 14 from 4 to 8
deep = 13 from 8 to 7
deep = 12 from 8 to 0
deep = 11 from 7 to 0
deep = 10 from 0 to 9
deep = 9 from 9 to 13
deep = 8 from 13 to 7
deep = 7 from 13 to 2
deep = 6 from 8 to 6
deep = 5 from 7 to 6
deep = 4 from 6 to 0
deep = 3 from 0 to 10
deep = 2 from 10 to 1
deep = 1 from 6 to 1
deep = 0 from 2 to 0

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值