-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontent.json
More file actions
executable file
·1 lines (1 loc) · 80.2 KB
/
content.json
File metadata and controls
executable file
·1 lines (1 loc) · 80.2 KB
1
{"meta":{"title":"LTitan`s Blog","subtitle":"foolish","description":"good good study, day day up!","author":"lyf","url":"https://LTitan.github.io"},"pages":[{"title":"about","date":"2018-10-25T01:04:16.000Z","updated":"2018-10-25T01:04:16.000Z","comments":true,"path":"about/index.html","permalink":"https://LTitan.github.io/about/index.html","excerpt":"","text":""},{"title":"categories","date":"2018-10-25T01:04:11.000Z","updated":"2018-10-25T01:04:11.000Z","comments":true,"path":"categories/index.html","permalink":"https://LTitan.github.io/categories/index.html","excerpt":"","text":""},{"title":"友情链接","date":"2018-10-25T01:27:04.000Z","updated":"2018-10-25T01:27:04.000Z","comments":true,"path":"links/index.html","permalink":"https://LTitan.github.io/links/index.html","excerpt":"","text":""},{"title":"tags","date":"2018-10-25T01:03:59.000Z","updated":"2018-10-25T01:03:59.000Z","comments":true,"path":"tags/index.html","permalink":"https://LTitan.github.io/tags/index.html","excerpt":"","text":""}],"posts":[{"title":"手指数字识别","slug":"手指数字识别","date":"2019-02-25T11:50:56.000Z","updated":"2019-02-25T12:45:18.000Z","comments":true,"path":"2019/02/25/手指数字识别/","link":"","permalink":"https://LTitan.github.io/2019/02/25/手指数字识别/","excerpt":"","text":"准备工作 家里面闲置一台手机,手机的像素还算不错,于是就想利用手机的摄像功能代替摄像头,来收集拍摄些图片进行深度学习。 设备展示 这个手机被我用钉子固定在一块木板上了 通过droidcam软件,进行局域网视频传输 手指数字拍摄 捣鼓这个东西太费时间太费力了,因为一个人的工作,我一边调动作一边拍照,弄了一下午胳膊也麻了。 最后0-5个数字分别拍了60张,成果如下(之前没有截图,现在剩下我处理过的了都是100*100的照片了): 利用opencv浅层处理 拍完后的照片都是640*480格式大小的,最后用opencv简单的resize成100 * 100的了 1234// 先用代码读取改路径下的所有照片名称图片 = cv::imread(路径)cv::resize(图片,保存的图像,cv::Size(100,100))cv::imwrite(路径, 保存的图像) 模型搭建 此模型采用的是自己瞎想的卷积神经网络,用了VGG的思想,但是最后结果不尽心意,抽时间改善模型 模型代码及结果 结果只有35%的成效,看来是模型很不行(笑哭),下次用用VGG-16试试水 1234import tensorflow as tfimport numpy as npimport tf_utilsimport MyData 1X_train,y_train = MyData.load_datas() 1print(y_train.shape) (360, 1) 123def weight(shape, stddev, name): inital = tf.truncated_normal(shape, stddev=stddev) return tf.Variable(inital, name=name) 123def bais(length, value, name): inital = tf.constant(value, shape=length) return tf.Variable(inital, name=name) 123def convert_to_one_hot(Y, C): Y = np.eye(C)[Y.reshape(-1)] return Y 12def conv2d(x, W, stride, pad): return tf.nn.conv2d(x, W, strides=stride, padding=pad) 12def max_pool_2d(x, ksize, stride): return tf.nn.max_pool(x, ksize=ksize, strides= stride, padding='SAME') 1m, height, width, channels = X_train.shape 12X = tf.placeholder(tf.float32, [None, height, width, channels], name='X')y_true = tf.placeholder(tf.float32, [None, 6], name='y_true') 12345678conv_W_b = { 'W1':weight([9, 9, channels, 10], 0.5, 'W1'), 'b1':bais([10], 0.5, 'b1'), 'W2':weight([7, 7, 10, 16], 0.5, 'W2'), 'b2':bais([16], 0.5, 'b2'), 'W3':weight([7, 7, 16, 32], 0.5, 'W3'), 'b3':bais([32], 0.5, 'b3'),} 12h_conv1 = tf.nn.relu(conv2d(X, conv_W_b['W1'], [1,1,1,1], 'VALID') + conv_W_b['b1'])h_pool1 = max_pool_2d(h_conv1, [1,2,2,1], [1,2,2,1]) 12h_conv2 = tf.nn.relu(conv2d(h_pool1, conv_W_b['W2'], [1,1,1,1], 'VALID') + conv_W_b['b2'])h_pool2 = max_pool_2d(h_conv2, [1,2,2,1], [1,2,2,1]) 12h_conv3 = tf.nn.relu(conv2d(h_pool2, conv_W_b['W3'], [1,1,1,1], 'VALID') + conv_W_b['b3'])h_pool3 = max_pool_2d(h_conv3, [1,2,2,1], [1,2,2,1]) 12345678910fc_W_b = { 'W_fc1':weight([7*7*32, 512], 0.5, 'W_fc1'), 'b_fc1':bais([512], 0.1, 'b_fc1'), 'W_fc2':weight([512, 512], 0.5, 'W_fc2'), 'b_fc2':bais([512], 0.1, 'b_fc2'), 'W_fc3':weight([512,64], 0.5, 'W_fc3'), 'b_fc3':bais([64], 0.1, 'b_fc3'), 'W_out':weight([64, 6], 0.5, 'W_out'), 'b_out':bais([6], 0.1, 'b_out')} 12h_flat = tf.reshape(h_pool3, [-1, 7*7*32])h_fc1 = tf.nn.relu(tf.matmul(h_flat, fc_W_b['W_fc1']) + fc_W_b['b_fc1']) 12keep_prob = tf.placeholder(tf.float32)h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob) 12h_fc2 = tf.nn.relu(tf.matmul(h_fc1_drop, fc_W_b['W_fc2']) + fc_W_b['b_fc2'])h_fc2_drop = tf.nn.dropout(h_fc2, keep_prob) 12h_fc3 = tf.nn.relu(tf.matmul(h_fc2_drop, fc_W_b['W_fc3']) + fc_W_b['b_fc3'])h_fc3_drop = tf.nn.dropout(h_fc3, keep_prob) 1y_out = tf.nn.softmax(tf.matmul(h_fc3_drop, fc_W_b['W_out']) + fc_W_b['b_out'], name='output') 123456789def next_batch(train_data, train_target, batch_size): index = [ i for i in range(0,len(train_target)) ] np.random.shuffle(index); batch_data = []; batch_target = []; for i in range(0,batch_size): batch_data.append(train_data[index[i]]); batch_target.append(train_target[index[i]]) return batch_data, batch_target 12345cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=y_out, labels=y_true)cost = tf.reduce_mean(cross_entropy)optimizer = tf.train.AdamOptimizer(0.0008).minimize(cost)correct_prediction = tf.equal(tf.argmax(y_out, 1), tf.argmax(y_true, 1))accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) 1234y_train = convert_to_one_hot(y_train, 6)#y_test = convert_to_one_hot(y_test, 6)X_train = X_train / 255#X_test = X_test / 255 12 123456789101112with tf.Session() as sess: sess.run(tf.global_variables_initializer()) for i in range(2000): batch_x, batch_y = next_batch(X_train, y_train, 16) np.random.seed(i) _, c = sess.run([optimizer, cost], feed_dict={X:batch_x, y_true:batch_y, keep_prob:0.5}) if (i%100 == 0 or i==1999) and i!=0: acc = sess.run(accuracy,feed_dict={X:batch_x, y_true:batch_y, keep_prob:1.0}) print('迭代%d 正确率为%f' %(i+1, acc)) # acc = sess.run(accuracy,feed_dict={X:X_test, y_true:y_test, keep_prob:1.0}) # print('测试集准确率:%f 损失率%f'%(acc, c)) 迭代101 正确率为0.187500 迭代201 正确率为0.375000 迭代301 正确率为0.187500 迭代401 正确率为0.125000 迭代501 正确率为0.125000 迭代601 正确率为0.312500 迭代701 正确率为0.312500 迭代801 正确率为0.125000 迭代901 正确率为0.062500 迭代1001 正确率为0.187500 迭代1101 正确率为0.125000 迭代1201 正确率为0.125000 迭代1301 正确率为0.000000 迭代1401 正确率为0.125000 迭代1501 正确率为0.312500 迭代1601 正确率为0.125000 迭代1701 正确率为0.250000 迭代1801 正确率为0.250000 迭代1901 正确率为0.125000 迭代2000 正确率为0.187500","categories":[],"tags":[{"name":"DeepLearning","slug":"DeepLearning","permalink":"https://LTitan.github.io/tags/DeepLearning/"}]},{"title":"TensorFlow 使用及手写字体训练","slug":"tensorflow","date":"2018-12-02T11:30:22.000Z","updated":"2018-12-02T11:53:18.530Z","comments":true,"path":"2018/12/02/tensorflow/","link":"","permalink":"https://LTitan.github.io/2018/12/02/tensorflow/","excerpt":"","text":"tensorflow入门 TensorFlow™ 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。它灵活的架构让你可以在多种平台上展开计算,例如台式计算机中的一个或多个CPU(或GPU),服务器,移动设备等等。TensorFlow 最初由Google大脑小组(隶属于Google机器智能研究机构)的研究员和工程师们开发出来,用于机器学习和深度神经网络方面的研究,但这个系统的通用性使其也可广泛用于其他计算领域。(复制过来的) 中文社区链接:链接 安装: python3: 终端下执行: pip(或者pip3) install --upgrade tensourflow 优势: 我们在进行深度学习的使用往往会进行后向传播,后向传播的实质是根据神经元微分求解,而tensorflow则不用考虑微积分,只实现前向传播就能运行。 f(b, c) = (b+3)*(b+c) 用tensorflow实现此函数(jupyter写的有输出内容),如下: 单个值测试 12345import tensorflow as tf #导入tf包import numpy as np #导入数组包'''用tensorflow 实现 f(c, b) = (b+3)*(b+c)''' '\\n用tensorflow 实现 f(c, b) = (b+3)*(b+c)\\n' 12b = tf.Variable(2., name='b') #定义一个2.0变量为bc = tf.Variable(3., name='c') #定义一个3.0变量为c 12345#不妨设中间变量 e = (b+3) ,d = (b+c) ,a= f(b, c)吧three = tf.constant(3., name='three') #定义一个常量3e = tf.add(b,three)d = tf.add(b, c) 12a = tf.multiply(e, d) #乘法print(a) #结果不是25 而是一个Tensor Tensor("Mul:0", shape=(), dtype=float32) 12345678#真实的运行结果如下:init_top = tf.global_variables_initializer() #初始化变量with tf.Session() as sess: #打开一个tensor会话 sess.run(init_top) #顺序不能变,先运行初始化 f = sess.run(a) #运行tensor a print(f) sess.close() #关闭会话 25.0 多组值测试 这时候我们用到tensorflow的容器 12import tensorflow as tf #导入tf包import numpy as np #导入数组包 12b = tf.placeholder(tf.float32, [None, 1],name='b') #定义一个b容器,大小可以不确定,[3,1]即为3个元素,None,为未知,来源数据大小c = tf.Variable(3., name='c') #定义一个3.0变量为c 1234#不妨设中间变量 e = (b+3) ,d = (b+c) ,a= f(b, c)吧three = tf.constant(3., name='three') #定义一个常量3e = tf.add(b,three)d = tf.add(b, c) 12a = tf.multiply(e, d) #乘法print(a) #结果不是25 而是一个Tensor Tensor("Mul:0", shape=(?, 1), dtype=float32) 123456789#真实的运行结果如下:b_array = np.array([1,2,3,4,5]).reshape(5,1) #创建一个b的元素数组init_top = tf.global_variables_initializer() #初始化变量with tf.Session() as sess: #打开一个tensor会话 sess.run(init_top) #顺序不能变,先运行初始化 f = sess.run(a,feed_dict={b: b_array}) #运行tensor a, 喂食词典 b的元素 来源于 b_array print(f) #输出必然是多组值 sess.close() #关闭会话 [[16.] [25.] [36.] [49.] [64.]] 好了函数介绍就到这 MNIST 手写字体训练 注释解释的很清楚,推荐有基础的人去看,新手会完全看不懂的 123456789import tensorflow as tffrom tensorflow.examples.tutorials.mnist import input_dataimport numpy as npimport matplotlib.pyplot as plt%matplotlib inlineplt.rcParams['figure.figsize'] = (13.0, 11.0) #设置plt的大小plt.rcParams['image.interpolation'] = 'nearest'plt.rcParams['image.cmap'] = 'gray' 1mnist = input_data.read_data_sets('MNIST_data/', one_hot=True) #加载mnist数据集 Extracting MNIST_data/train-images-idx3-ubyte.gz Extracting MNIST_data/train-labels-idx1-ubyte.gz Extracting MNIST_data/t10k-images-idx3-ubyte.gz Extracting MNIST_data/t10k-labels-idx1-ubyte.gz 12345learning_rate = 0.05 #学习率epochs = 20 #迭代大次数batch_size = 50 #迭代小次数,也是每次训练数据集的数据量 总次数 20*50x = tf.placeholder(tf.float32, [None, 784], name='x') #定义变量x 原图为64*64的数据y = tf.placeholder(tf.float32, [None, 10], name='y') # 12345678910#设置了3层神经元结构 第一次为300个 第二层 200个 最后输出层 10W1 = tf.Variable(tf.random_normal([784, 300], stddev=0.01), name='W1')#tf的正则化表示,不了解的可以忽略下行tf.add_to_collection('losses', tf.contrib.layers.l2_regularizer(0.005)(W1))b1 = tf.Variable(tf.random_normal([300]), name='b1')W2 = tf.Variable(tf.random_normal([300, 200], stddev=0.01), name='W2')tf.add_to_collection('losses', tf.contrib.layers.l2_regularizer(0.005)(W2))b2 = tf.Variable(tf.random_normal([200]), name='b2')W3 = tf.Variable(tf.random_normal([200, 10], stddev=0.01), name='W3')b3 = tf.Variable(tf.random_normal([10]), name='b3') 12345hd_out1 = tf.add(tf.matmul(x,W1), b1)hd_out1 = tf.nn.relu(hd_out1) #第一层的输出 用的是relu函数hd_out2 = tf.add(tf.matmul(hd_out1,W2), b2)hd_out2 = tf.nn.relu(hd_out2) #第二层的输出 1y_ = tf.nn.softmax(tf.add(tf.matmul(hd_out2, W3), b3)) #最终输出 个数为10 1234y_clipped = tf.clip_by_value(y_, 1e-10, 0.9999999) #过滤cross_entropy = -tf.reduce_mean(tf.reduce_sum(y*tf.log(y_clipped)+(1-y)*tf.log(1-y_clipped), axis=1)) #求解交叉熵tf.add_to_collection('losses', cross_entropy)loss = tf.add_n(tf.get_collection('losses')) #将交叉熵放入losses集,下面会用到 12optimizer = tf.train.GradientDescentOptimizer(learning_rate)train = optimizer.minimize(loss) #选用梯度下降算法,来进行学习,选用losses里面的最小值 12345init_top = tf.global_variables_initializer()correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) #求正确率#保存模型saver = tf.train.Saver() 123456789101112131415161718with tf.Session() as sess: sess.run(init_top) total_batch = int(len(mnist.train.labels)/ batch_size) costs = [] for ep in range(epochs): avg_cost = 0 for i in range(total_batch): batch_x, batch_y = mnist.train.next_batch(batch_size) _t, c = sess.run([train, cross_entropy], feed_dict={x:batch_x, y:batch_y}) avg_cost += c/total_batch print('epoch%d,cost=%f'% (ep+1,avg_cost)) costs.append(avg_cost) print(sess.run(accuracy, feed_dict={x: mnist.test.images, y: mnist.test.labels})) saver.save(sess,'./', global_step=epochs) #保存模型 sess.close() costs = np.squeeze(costs) plt.plot(costs) plt.show() epoch1,cost=1.764043 epoch2,cost=0.553732 epoch3,cost=0.377033 epoch4,cost=0.296909 epoch5,cost=0.250849 epoch6,cost=0.222690 epoch7,cost=0.203826 epoch8,cost=0.187270 epoch9,cost=0.176289 epoch10,cost=0.169246 epoch11,cost=0.160853 epoch12,cost=0.156802 epoch13,cost=0.151215 epoch14,cost=0.144273 epoch15,cost=0.142850 epoch16,cost=0.134713 epoch17,cost=0.135944 epoch18,cost=0.133344 epoch19,cost=0.129857 epoch20,cost=0.126890 0.9725 emmm, 蜜汁随缘调参职能到97.25%了","categories":[],"tags":[{"name":"DeepLearning","slug":"DeepLearning","permalink":"https://LTitan.github.io/tags/DeepLearning/"}]},{"title":"个人逆向分析","slug":"逆向分析","date":"2018-11-20T09:00:22.000Z","updated":"2018-11-21T12:54:43.800Z","comments":true,"path":"2018/11/20/逆向分析/","link":"","permalink":"https://LTitan.github.io/2018/11/20/逆向分析/","excerpt":"","text":"前言 在学习生活种,我们可能会觉得学习生活日常枯燥无聊,身为计算机的学习者我们会时常问计算机到底是来干啥的,(除了做一些乘除数学运算和做一些商业网页还能干些什么?),现在我将用我第一人称的角度来分析逆向数据和某些网站的信息采集,以此来提高学习生活的兴趣,加深对计算机某领域的了解(下面的操作内容我建议可以一步一去跟着操作)。 下面可能会用到的工具有 FireFox浏览器(或者chrome浏览器) 抓包工具(Fiddler4) 下载链接 你熟悉的编程语言 谏言 不要用某些工具或者耍小聪明去做一些法律不允许的行为 HTTP请求协议 由于本人也只是凭借着经验才学习到的模糊知识,所以给你的建议是去学习一些专业的网络知识,如果我去解释阐释可能会产生一些误解,反而起到误导效果,在这里我就给大家推荐去了解http如何如何运行的,URL是什么,请求的方式有哪些以及一些常用的网络参数…可以参考别人的博客链接 学校教务处登录分析过程 大家都知道教务处查成绩啊,查课表每次都很麻烦,所以能不能用一个简单的方式去实现,(上完满满的一天课,回到寝室打开电脑,又打开了卡卡的浏览器去登教务处,心里是不是特别烦躁/偷笑),这时候就要把我们的脑洞和编程掏出来了。 fiddler的使用 先打开我们的fiddler软件是这样的: 这时候不要着急,把没用的数据按键盘Ctrl+a,再按delete键全部删掉,再打开火狐或者chrome,进行下面操作(把箭头拖到浏览器上,就能锁定抓取进程了): 下面是打开网页进行浏览测试: 这时我们点开第一条数据,就是网页的整个http请求过程: 123表示步骤,你可以认真看看headers里的信息,因为下面我们会用编程去实现这个过程,其中黄框是整个请求信息,紫框是真个响应信息。 登录测试 我将会用我自己的信息登录,如下图: fiddler查看整个信息: 发现网页跳转了????很纳闷这个网页使用来干啥的,于是我们来查看这个网页发送了啥数据: 查看表单,结果发现了encode一个表单,然后又开始好奇了???是不是这个就是登录的账号和密码的加密鸭,我们接着进行分析: 查看网页源码 分析源码,找到学号和密码那两个输入框的源码,会发现标签会有id的吧,这个不用说也就懂,js可以通过id来获取用户的输入信息进行绑定: 然后我们往后面接着找有没有什么js程序来处理这些信息呢? 这时候我们有了大大的收获(我们找到了上面留下来的疑问encode): 源码意思呢就是先判断学号和密码是否为空,再进行了一个encodeInp函数,最后生成了一个学号+%%%+密码的字符串进行登录验证的,哈哈哈,那么登录就解决了 加密和解密 这是两个难兄难弟,正如你的qq登录一样,现在的进行安全认证都是得加密的,随然上面我们知道了登录字符串的由来,但是他也不是我们原来的学号啊,是一大串啊,下面我们将解密encodeInp: 返回到源码的最前端,我们去找js的源码: 点这个js链接,按Ctrl+F查找字符encodeInp(结果还真查找到了): 这个js源码大家可能啃不动,所以我也不卖关子了,在这里我就直接说好了,源码的内容就是Base64加密,对比如下: 你会神奇的发现我的学号机密后和encode前部分是一模一样的,所以加密也告一段落 编程实现 上面呢我们分析了教务处的后台原来是用encode验证登录的,那么登录后不是一个302网页吗,又不是教务处登进去的首界面,那么分析又来了,现在大部分的网站后台是根据登录后给你一个cookie信息,后台在验证的时候会一直用当前用户的cookie信息是否合法来验证,验证不过去就访问不了界面,(哈哈,这是我凭借经验得出来的,其他的验证我还没怎么留意,欢迎补充)。所以教务处的整体架构呢,就是用302网页来获取cookie信息,再转到别的网页去让学生浏览。cookie如下: 好了,下面就用python进行登录一下测试: 123456789101112131415161718192021222324252627282930313233343536373839#用python的bease64库来进行加密编码import base64#用requests模块来进行请求处理import requestsstudent_number = input('输入学号:\\n')student_password = input('输入密码\\n')#base64编码后的学号 输入的信息转为utf-8code_stuNumber = base64.b64encode(student_number.encode('utf-8'))code_password = base64.b64encode(student_password.encode('utf-8'))login_url = "http://cdjwc.ccu.edu.cn/jsxsd/xk/LoginToXk" #登录的url,就是302的那个网址index_url = "http://cdjwc.ccu.edu.cn/jsxsd/framework/xsMain.jsp" #学生的首界面#为了方便保存cookie信息,我们这里用requests的session就是全局用一个会话来模拟浏览器访问,就不要额外去保存cookie了session = requests.session()#首部信息,标志的操作系统和浏览器的信息header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0'}#提交信息就是我们分析的 encode字符串post_data = {'encoded':code_stuNumber+b"%%%"+code_password}#登录try: re =session.post(login_url, headers = header, data=post_data) #进行post请求,加上我们的首部和encodeexcept: print('出错') exit(-1)#打印一下状态码print("登录状态"+str(re.status_code))#首页查看try: re = session.get(index_url, headers=header)except: print('出错') exit(-1)#打印一下你的网页源码,查看是否有你的名字,有就是模拟成功了print(re.text) PS:(暂时先更新教务处的,有时间就更新别的网站测试)","categories":[],"tags":[{"name":"other","slug":"other","permalink":"https://LTitan.github.io/tags/other/"}]},{"title":"基于ORB的图像矫正","slug":"基于orb的图像矫正","date":"2018-07-25T04:30:22.000Z","updated":"2018-10-25T02:01:40.000Z","comments":true,"path":"2018/07/25/基于orb的图像矫正/","link":"","permalink":"https://LTitan.github.io/2018/07/25/基于orb的图像矫正/","excerpt":"","text":"单应性矩阵 在二维平面上,平面的单应性被定义为从一个平面到另一个平面的投影映射,就像我们拍一张照片时,从我们眼前的点一一映射到摄像机内部一样。英文学名叫做homography(单应性,ps:下面的鲁棒性就是稳定性。 下即为一个简单的3x3homo矩阵 \\left[ \\begin{matrix} h00 & h01 & h02 \\ h10 & h11 & h12 \\ h20 & h21 & h22 \\end{matrix} \\right ] \\tag{1} 其平面坐标变换对应为(原坐标为(x1,y1),其变换为(x2,y2)): [x1 y1 1]=H[x2 y2 1]=[h00h01h02 h10h11h12 h20h21h22][x2 y2 1]\\left[ \\begin{array}{c} x_1 \\ y_1 \\ 1 \\end{array} \\right] = H \\left[ \\begin{array}{c} x_2 \\ y_2 \\ 1 \\end{array} \\right] = \\left[ \\begin{array}{ccc} h{00} & h{01} & h{02} \\ h{10} & h{11} & h{12} \\ h{20} & h{21} & h_{22} \\end{array} \\right] \\left[ \\begin{array}{c} x_2 \\ y_2 \\ 1 \\end{array} \\right] [x1 y1 1]=H[x2 y2 1]=[h00h01h02 h10h11h12 h20h21h22][x2 y2 1] 下图为变换的例子 在OpenCV里面,单应矩阵的计算函数为 : Mat findHomography(inputArray points1,inputArray points2,int method) . 参数一为输入的x1,y1 点集 . 参数二为输入的 x2,y2 点集 . 参数三为计算方法:0 - 使用所有点的常规方法 RANSAC - 基于RANSAC的鲁棒方法 LMEDS - 最小中值稳健方法 RHO - 基于PROSAC的鲁棒方法 #关键点检测 上篇里有介绍基于ORB的关键点检测,舒服的是这个算法是至今最好最快的算法,其他的检测算法还有SURF,SIFT,etc….算法,效率而讲,500点一下还是差不多的,但是点越多的话ORB绝对胜出。 #关键点匹配 上篇的文章依然用着老的api,但是编译了最新的3.4之后api又发生了变化(mmp),这次能直接detect 和compute一起用了。 其用到头文件 xfeatures2d/nonfree.hpp 命名空间 cv::xfeatures2d 检测器创建如下,只能处理灰度图像 1Ptr<Feature2D> orb = ORB::create(MAX_FEATURES);//智能指针传参为最大检测点数 图像矫正如下: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172#include <opencv2/opencv.hpp>#include <opencv2/xfeatures2d/nonfree.hpp>#include <opencv2/features2d.hpp>using namespace std;using namespace cv;using namespace cv::xfeatures2d;const int MAX_FEATURES = 500;const float GOOD_MATCH_PERCENT = 0.15f;void alignImage(Mat& img1, Mat& img2, Mat& dst, Mat h){ Mat img1Gray, img2Gray;//灰度图 cvtColor(img1, img1Gray, COLOR_BGR2GRAY); cvtColor(img2, img2Gray, COLOR_BGR2GRAY);//灰度处理 vector<KeyPoint> keypoints1, keypoints2;//图1 关键点 图2 关键点 Mat descriptors1, descriptors2; Ptr<Feature2D> orb = ORB::create(MAX_FEATURES); orb->detectAndCompute(img1Gray, Mat(), keypoints1, descriptors1);//检测匹配 orb->detectAndCompute(img2Gray, Mat(), keypoints2, descriptors2);//检测匹配 vector<DMatch> matches; Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming"); matcher->match(descriptors1, descriptors2, matches, Mat());//匹配描线 sort(matches.begin(), matches.end());//排序筛选优好的点 const int numGoodMatches = matches.size()*GOOD_MATCH_PERCENT; matches.erase(matches.begin() + numGoodMatches, matches.end()); Mat imMatches; drawMatches(img1, keypoints1, img2, keypoints2, matches, imMatches); imshow("match", imMatches); imwrite("F:\\\\match.jpg", imMatches); vector<Point2f> points1, points2; for (auto x : matches) { points1.push_back(keypoints1[x.queryIdx].pt); points2.push_back(keypoints2[x.trainIdx].pt); } h = findHomography(points1, points2, RANSAC); warpPerspective(img1, dst, h, img2.size());//旋转矫正}int main(){ string refFilename="F:\\\\picture\\\\src.jpg";//原图路径 Mat src = imread(refFilename); string scanFilename = "F:\\\\picture\\\\scanned.jpg";//扫描的或者电子版的图 Mat scan_src = imread(scanFilename); Mat imageRege, homo;//完成图像 和 homo矩阵 alignImage(src, scan_src, imageRege, homo); cout << homo << endl; imshow("dst", imageRege); imwrite("F:\\\\change.jpg", imageRege); waitKey();}","categories":[],"tags":[{"name":"opencv","slug":"opencv","permalink":"https://LTitan.github.io/tags/opencv/"}]},{"title":"opencv:基于ORB特征点匹配及图像拼接","slug":"opencv-基于ORB特征点匹配及图像拼接","date":"2018-07-21T02:18:59.000Z","updated":"2018-09-25T09:25:38.000Z","comments":true,"path":"2018/07/21/opencv-基于ORB特征点匹配及图像拼接/","link":"","permalink":"https://LTitan.github.io/2018/07/21/opencv-基于ORB特征点匹配及图像拼接/","excerpt":"","text":"#ORB简述 ORB(Oriented FAST and Rotated BRIEF)是一种快速特征点提取和描述的算法。ORB算法分为两部分,分别是特征点提取和特征点描述。特征提取是由FAST(Features from Accelerated Segment Test)算法发展来的,特征点描述是根据BRIEF(Binary Robust IndependentElementary Features)特征描述算法改进的。ORB特征是将FAST特征点的检测方法与BRIEF特征描述子结合起来,并在它们原来的基础上做了改进与优化。 下图是我在高数课本上进行的测试: #特征点匹配 例子里采集了3000个点吧对比一下,opencv3.3以后好像用上了智能指针Ptr对象,对象的创建只能用ptr模板创建,大概是因为图像再处理的过程中防止异常错误正常抛出吧。 两张原图: 123456789101112131415161718192021222324252627282930313233343536373839404142Mat image_src1,image_src2; Mat image_gray1,image_gray2; image_src1=imread("D:\\\\yy.jpg"); cvtColor(image_src1,image_gray1,COLOR_BGR2GRAY);//转为灰度图 image_src2=imread("D:\\\\zz.jpg"); cvtColor(image_src2,image_gray2,COLOR_BGR2GRAY);//转为灰度图 Ptr<Feature2D> oDetectors=ORB::create(3000);//创建orb对象,采集特征点 vector<KeyPoint> KeyPoints1,KeyPoints2;//关键点数组 oDetectors->detect(image_gray1,KeyPoints1);//采集左图特征点 oDetectors->detect(image_gray2,KeyPoints2);//采集右图特征点 Mat imageDec1,imageDec2; Ptr<Feature2D> ptor=ORB::create();//创建一个新的oeb对象,当然也可以用上面那个 ptor->compute(image_gray1,KeyPoints1,imageDec1);//描述特征点 ptor->compute(image_gray2,KeyPoints2,imageDec2);//描述特征点 flann::Index flannIndex(imageDec1,LshIndexParams(12,20,2), cvflann::FLANN_DIST_HAMMING); vector<DMatch> goodMatchPoints; Mat matchIndex(imageDec2.rows,2,CV_32SC1); Mat matchDistance(imageDec2.rows,2,CV_32FC1); flannIndex.knnSearch(imageDec2,matchIndex,matchDistance,2,flann::SearchParams());//采用k近似值 将匹配的索引和距离计算出来 for(int i=0;i < matchDistance.rows;i++) { float t=matchDistance.at<float>(i,0); float s=matchDistance.at<float>(i,1); if(t<s*0.46)//相似度大约为0.46就算相符 { DMatch part_matches(i,matchIndex.at<int>(i,0),t); goodMatchPoints.push_back(part_matches); } } Mat first_matching; drawMatches(image_src2,KeyPoints2,image_src1,KeyPoints1, goodMatchPoints,first_matching); imshow("Match",first_matching); 如图,匹配完成: #拼接 记录相似点,将另一图像适度旋转调整,然后拼接 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071vector<Point2f> Conners(4);// 左上角 左下角 右上角 右下角void calcCorners(const Mat& homo,const Mat& input){ double v2[]={0,0,1}; double v1[3];//改变后值 Mat V2=Mat(3,1,CV_64FC1,v2); Mat V1=Mat(3,1,CV_64FC1,v1); //下面都是重复代码计算看懂一个即可 V1=homo*V2; Conners[0].x=v1[0]/v1[2]; Conners[0].y=v1[1]/v1[2]; v2[0]=0; v2[1]=input.rows; v2[2]=1; V2=Mat(3,1,CV_64FC1,v2); V1=Mat(3,1,CV_64FC1,v1); V1=homo*V2; Conners[1].x=v1[0]/v1[2]; Conners[1].y=v1[1]/v1[2]; v2[0]=input.cols; v2[1]=0; v2[2]=1; V2=Mat(3,1,CV_64FC1,v2); V1=Mat(3,1,CV_64FC1,v1); V1=homo*V2; Conners[2].x=v1[0]/v1[2]; Conners[2].y=v1[1]/v1[2]; v2[0]=input.cols; v2[1]=input.rows; v2[2]=1; V2=Mat(3,1,CV_64FC1,v2); V1=Mat(3,1,CV_64FC1,v1); V1=homo*V2; Conners[3].x=v1[0]/v1[2]; Conners[3].y=v1[1]/v1[2];}main:://此处接着标题二的代码vector<Point2f> image_points1,image_points2; for(auto x:goodMatchPoints) { image_points2.push_back(KeyPoints2[x.queryIdx].pt); image_points1.push_back(KeyPoints1[x.trainIdx].pt); } Mat homo=findHomography(image_points1,image_points2,CV_RANSAC); cout<<"homo matrix:"<<homo<<endl; calcCorners(homo,image_src1); Mat imageTransform1,imageTransform2; warpPerspective(image_src1,imageTransform1,homo,Size(max(Conners[2].x, Conners[3].x),image_src2.rows)); imshow("12",imageTransform1); int dst_width=imageTransform1.cols; int dst_height=image_src2.rows; Mat dst(dst_height,dst_width,CV_8UC3); dst.setTo(0); imageTransform1.copyTo(dst(Rect(0,0,imageTransform1.cols,imageTransform1.rows))); image_src2.copyTo(dst(Rect(0,0,image_src2.cols,image_src2.rows))); imshow("pin",dst); 拼接完,如图: #优化拼接处 此处是借鉴别人的,因为自己也不可能凭空想到,哈哈,修复拼接处的线条问题。 1234567891011121314151617181920212223242526void optimizated(Mat &src,Mat &trans,Mat &dst){ int start=min(Conners[0].x,Conners[1].x); double processWidth=src.cols-start; int rows=dst.rows; int cols=src.cols; double alpha=1; for(int i=0;i<rows;++i) { uchar *p=src.ptr<uchar>(i); uchar *t=trans.ptr<uchar>(i); uchar *d=dst.ptr<uchar>(i); for(int j=start;j<cols;++j) { int k=j*3; if(t[k]==0&&t[k+1]==0&&t[k+2]==0) alpha=1; else alpha=(processWidth-j+start)/processWidth; d[k]=p[k]*alpha+t[k]*(1-alpha); d[k+1]=p[k+1]*alpha+t[k+1]*(1-alpha); d[k+2]=p[k+2]*alpha+t[k+2]*(1-alpha); } }} 成品图效果如下:","categories":[],"tags":[{"name":"opencv","slug":"opencv","permalink":"https://LTitan.github.io/tags/opencv/"}]},{"title":"数据结构(四) 双向循环链表","slug":"数据结构(四) 双向循环链表 ","date":"2018-07-16T05:16:59.000Z","updated":"2018-10-25T01:59:30.000Z","comments":true,"path":"2018/07/16/数据结构(四) 双向循环链表 /","link":"","permalink":"https://LTitan.github.io/2018/07/16/数据结构(四) 双向循环链表 /","excerpt":"","text":"又来安利福利了,双向和循环一起了吧,单讲双向没意思,结合在一起就有意思了,看完了第一篇的单链表应该看双向很轻松了。 运行截图 遗憾没有放上查找,请自己加上吧,正序找逆序找都是easy的 代码如下: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132#include <stdio.h>#include <stdlib.h>#include <malloc.h>//双向循环链表 基本操作和单链表差不多,只不过他有自己的回头路了typedef struct _double_linked_list{ int data;//存放数据 struct _double_linked_list *front;//前指针 struct _double_linked_list *next;//后指针}DouLinkedList;void traversing_postive(DouLinkedList *list)//正序遍历从头到尾{ DouLinkedList *p=list->next; DouLinkedList *head=list;//起始头位置 while(p!=head)//循环到头部就停止 { printf("%d ",p->data); p=p->next; } printf("\\n");}void traversing_reverse(DouLinkedList *list){ DouLinkedList *p=list->front; DouLinkedList *head=list; while(p!=head)//循环到头部就停止 { printf("%d ",p->data); p=p->front; } printf("\\n");}void create_tail(DouLinkedList *list,const int n)//n还是创建的长度{ DouLinkedList *p=list; DouLinkedList *head=list; p->front=NULL; p->next=NULL; for(int i=0;i<n;++i) { DouLinkedList *temp; int data; temp=(DouLinkedList*)malloc(sizeof(DouLinkedList)); printf("请输入第%d个结点的数据\\n",i+1); scanf("%d",&data); temp->data=data; head->front=temp;//头的前面指向插入的尾部 temp->next=head;//尾的下一个指向头部 这样就构成循环链表了 p->next=temp; temp->front=p; p=temp; }}int insert_node(DouLinkedList *list,int pos,int data){ //链表的下标从1开始 if(pos<1||list==NULL) return -1;//位置小于或者链表是空的 插入失败 DouLinkedList *head=list; DouLinkedList *p=list->next; int i=1;//计数i while(p!=head) { if(i==pos) break;//找到前驱点就停止 ++i; p=p->next; } if(i<pos) return -1;//越界 DouLinkedList *newNode=(DouLinkedList*)malloc(sizeof(DouLinkedList)); newNode->data=data; newNode->front=p->front; p->front->next=newNode; newNode->next=p; p->front=newNode; return 1; } int delete_node(DouLinkedList *list,int pos) { if(pos<1||list==NULL) return -1; DouLinkedList *p=list->next; DouLinkedList *head=list; int i=1; while(p!=head) { if(i==pos) break; ++i; p=p->next; } if(i<pos) return -1; p->front->next=p->next; p->next->front=p->front; free(p); return 1;}int main(){ DouLinkedList *mylist; mylist=(DouLinkedList*)malloc(sizeof(DouLinkedList));//新建一个头节点 create_tail(mylist,5); printf("正序遍历"); traversing_postive(mylist); printf("逆序遍历"); traversing_reverse(mylist); int flag=insert_node(mylist,2,666);//在第二个插入666 if(flag!=-1) puts("插入成功"); else puts("插入失败"); traversing_postive(mylist); flag=delete_node(mylist,3);//删除第三个 if(flag!=-1) puts("删除成功"); else puts("删除失败"); traversing_postive(mylist); return 0;}","categories":[],"tags":[{"name":"data-structure","slug":"data-structure","permalink":"https://LTitan.github.io/tags/data-structure/"}]},{"title":"数据结构(二) 字符串的常规使用","slug":"数据结构(二) 字符串的常规使用 ","date":"2018-07-16T01:44:59.000Z","updated":"2018-09-25T09:38:44.000Z","comments":true,"path":"2018/07/16/数据结构(二) 字符串的常规使用 /","link":"","permalink":"https://LTitan.github.io/2018/07/16/数据结构(二) 字符串的常规使用 /","excerpt":"","text":"虽然字符串简单,但是在生活中的用处确实很广,就像论文查重,文章检索,没有字符串的数据结构和算法是实现不了的 我在这先放上c语言版的char数组,文章的下篇将介绍c++版的(唉,这两天感觉自己特别能得吧了)。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <memory.h> //简单点说字符串就是字符数组 //基本运算无非就是长度 比较 回文 模式匹配(查找) //模式匹配和回文我将会单独开文章的,请耐心等等吧 = = //1.求字符串的长度 这里用到strlen函数 //那为什么不用sizeof(str)/sizeof(char) 计算呢? //原因是开辟了1024个长度不一定用满,用sizeof计算呢即为1024,而非字符串长度 int str_length(char *s) { return strlen(s);//直接用strlen求解 } //2.字符串比较 就是挨个比较字符串的字符 //可以用strcmp函数比较 也可以自己手写一个 int str_comparison_system(char *p,char *q)//函数意思为调用系统函数 { //返回1 即p大 0即一样大小 -1即q大 return strcmp(p,q); } int str_comparison_user(char *p,char *q)//这个比较函数为自己写的 { //先求出各自的长度 遍历长度最小的字符串比较,防止越界 //这是作者自己想到的,有什么好的想法欢迎来补充 //如果面试提到的话,自己可以借鉴一下 int lenp=str_length(p); int lenq=str_length(q); int theMin=lenp>lenq?lenq:lenp;//求出最短的长度 for(int i=0;i<theMin;++i) { if(p[i]==q[i]) continue; if(p[i]>q[i]) return 1; if(p[i]<q[i]) return -1; } return lenp>lenq?1:0;//这里的做法是为了防止p的前面与q一致,但是p后面有字符 //EG:abcdefg > abcd } int main() { char str1[1024]="hello world";//c语言中我们用char数组表示字符串 char str2[500]="hello friends"; printf("str1的长度:%d str2的长度:%d\\n",str_length(str1), str_length(str2)); //测试一下sizeof的区别 printf("str1的空间:%d\\n",sizeof(str1)/sizeof(char));//输出1024 //字符串系统和自己写的比较 printf("系统函数 比较结果:%d\\n",str_comparison_system(str1,str2)); printf("自己写函数 比较结果:%d\\n",str_comparison_user(str1,str2)); //小福利 送上汇编写的 memory 内存函数的使用 memset(str2,0,sizeof(str2));//内存设置 一般把数组清0用 /* 参数一是你要设置的数组首地址 参数二是字节填充大小 注意int是占8或4个字节全填上1的话 结果就肯定不是1了 参数三是你要填的字节长度 */ puts(str2); memcpy(str2,str1,sizeof(str2));//内存拷贝 // 与strcpy(str2,str1);大同小异 但实际上mem的效率是大于strcpy的 //参数一看就懂了 把str1拷贝给str2 但是最后的参数一定是谁短写谁,否则炸内存 puts(str2); puts(str1); return 0; }c++版的string 数据类型 #include <iostream> #include <string> using namespace std; int main() { string str1="hello world"; string str2="hello friends"; //哎 是不是有疑问了?不用考虑字串的空间大小么? //这里我解释:程序运行实在操作系统的堆上运行的,线程实在栈上运行的 //既然实在堆上运行的,就看你电脑内存了,所以你就可以不用管了 //string 可以用[] 下标访问 // string 的输入两种方式 空格能输入和不能输入 //接收空格的字符串输入 // getline(cin,str1);//意思就是接收一行 回车生效 //不能接收空格的输入 // cin>>str1; //求字符串的长度 string.length() cout<<"str1的长度为"<<str1.length()<<endl; //字符串比较 直接用 <、== 或 > 判断就行了 cout<<(str1>str2)<<endl; //子串 string sub=str1.substr(5,3);//从5的位置截取3个长度 wo cout<<sub<<endl; //查找 时间效率为O(nlog(n)) 原理就是用的模式匹配 int flag=str1.find("lyf");//找不到返回-1 找到返回下标 cout<<flag<<endl; //删除 n个 和 全部 str1.erase(3,4);//从3的位置删除4个 cout<<str1<<endl; str1.clear();//全部删除 cout<<str1<<endl; //加法 连接运算符 cout<<(str1+str2)<<endl; return 0; }","categories":[],"tags":[{"name":"data-structure","slug":"data-structure","permalink":"https://LTitan.github.io/tags/data-structure/"}]},{"title":"数据结构(三) 字符串模式匹配KMP和基于哈希的匹配","slug":"数据结构(三) 字符串模式匹配KMP和基于哈希的匹配","date":"2018-07-16T01:16:59.000Z","updated":"2018-09-25T09:37:00.000Z","comments":true,"path":"2018/07/16/数据结构(三) 字符串模式匹配KMP和基于哈希的匹配/","link":"","permalink":"https://LTitan.github.io/2018/07/16/数据结构(三) 字符串模式匹配KMP和基于哈希的匹配/","excerpt":"","text":"没想到吧,竟然是同一天写的,先安利一个kmp视频,看完之后你看代码就有感觉了。 但是还是感觉哈希流批,万物基于哈希(滑稽) ###链接 B站链接 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 #include <stdio.h> #include <stdlib.h> #include <string.h> //用c++的编译器吧,这里我用到了动态内存 int *next; void getNext(char* p) { int lenp = strlen(p); next[0] = -1; //第一个是未知的,就放上-1 int k = -1;//指针指在字符串最外面 int j = 0; //指针指在首地址上 while (j < lenp - 1) { //p[k]表示前缀,p[j]表示后缀 if (k == -1 || p[j] == p[k]) { ++k; ++j; next[j] = k; } else { k = next[k]; } } } int Kmp(char* s, char* p) { int i = 0; int j = 0; int lens = strlen(s); int lenp = strlen(p); while (i < lens && j < lenp) { //如果j = -1,或者当前字符匹配成功即 S[i] == P[j],都令指针移动 if (j == -1 || s[i] == p[j]) { i++; j++; } else { //如果j != -1,且当前字符匹配失败,则令 i 指针不懂,j 回退 j = next[j]; //next[j]即为j所对应的next值 } } if (j == lenp) return i - j; //返回下标 else return -1; } int main() { char str1[1024]="helloOWorldNotheloWhy??helhellelo"; char str2[1024]="hellelo"; next=new int[strlen(str2)+1];//next 数组长度应该比匹配串大一 getNext(str2); printf("%d",Kmp(str1,str2)); delete[] next; return 0; }哈希匹配 #include <stdio.h> #include <stdlib.h> #include <string.h> //基于哈希的模式匹配 typedef unsigned long long ULL;//用编译器最大的数据类型 2^64 const ULL HashConst=100000007;//哈希基数 mod hashConst int hash_machting(char *a,char *b)//这次是判a是否在b中出现 { int lena=strlen(a); int lenb=strlen(b); if(lena>lenb) return -1;//a太长了 ULL t=1; //计算哈希计数的lena次方 for(int i=0;i<lena;++i) t*=HashConst; //计算a和b为lena的前缀对应的哈希值 ULL ah=0,bh=0; for(int i=0;i<lena;++i) ah=ah*HashConst+a[i]; for(int i=0;i<lena;++i) bh=bh*HashConst+b[i]; //匹配 更新哈希值 for(int i=0;i+lena<=lenb;i++) { if(ah==bh) return i; if(i+lena<lenb) bh=bh*HashConst+b[i+lena]-b[i]*t; } return -1; } int main() { char str1[1024]="helloOWorldNotheloWhy??helhellelo"; char str2[1024]="hellelo"; printf("%d\\n",hash_machting(str2,str1)); return 0; }","categories":[],"tags":[{"name":"data-structure","slug":"data-structure","permalink":"https://LTitan.github.io/tags/data-structure/"}]},{"title":"数据结构(一)单链表、栈、和队列","slug":"数据结构(一)单链表、栈、和队列 ","date":"2018-07-15T04:16:59.000Z","updated":"2018-09-25T09:41:34.000Z","comments":true,"path":"2018/07/15/数据结构(一)单链表、栈、和队列 /","link":"","permalink":"https://LTitan.github.io/2018/07/15/数据结构(一)单链表、栈、和队列 /","excerpt":"","text":"看到实验室这么多考研的童鞋,自己觉得得去分享点东西把,把自己会的分享给他们吧。 这次只是单链表的创建、插入、删除、查找 单链表的c语言代码加详细注释,双向链表和循环链表待更新 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142#include <stdio.h>#include <stdlib.h>#include <malloc.h>//申请和释放内存用//前提说明 所有的i++ 用 ++i使用,i++需要额外的寄存器 ++i则反,提高运行效率//如有内存泄漏及溢出或者野指针情况 请及时提出//带头节点的单链表typedef struct _linked_list{ int data;//这里只是用了一个int当作测试数据,自己可以改 struct _linked_list *next;//尾指针}linkedList;//重新命名void traversing(linkedList *List)//遍历链表{ linkedList *p=List->next;//遍历指针p先指向list的第一个位置 而不是头节点位置 while(p!=NULL) { printf("%d ",p->data); p=p->next;//不断往下指 } printf("\\n");//最后换行}void create_tail(linkedList *List,const int n)//尾插法 n表示要插入的个数{ linkedList *tail=List; tail->next=NULL;//先将尾巴放在链表头的位置,然后才进行尾插 int temp;//输入值的中间变量 for(int i=0;i<n;++i) { printf("请输入第%d节点的数值\\n",i+1); scanf("%d",&temp);//输入数值 linkedList *newNode=(linkedList*)malloc(sizeof(linkedList));//申请一个新的节点 newNode->data=temp;//数值拷贝 tail->next=newNode;//尾巴的下一个指向新节点 newNode->next=NULL;//新节点后面没有节点了 tail=newNode;//新节点变成尾巴 }}int insert_node(linkedList *list,int pos,int data)//pos即插入位置 data即数值{ //原理基本上和创建的相似 //因为c语言没有bool类型 只能用int类型的返回值 //链表的下标是从1开始的,头节点(没有数值)算0下标,自己也可以修改 if(pos< 1|| list==NULL) return -1;//插入位置小于1 或者 链表为空 直接失败 linkedList *head=list;//先指向头节点 int i=0;//计数 找到插入位置的前驱 while(head!=NULL) { if(i==pos-1) break;//找到前驱结点就停止 ++i; head=head->next; } if(i<pos-1) return -1;//pos长度超过链表长度 插入失败 linkedList *newNode=(linkedList*)malloc(sizeof(linkedList));//新的节点 newNode->data=data; newNode->next=NULL; linkedList *temp;//中间无意义节点 temp=head->next; head->next=newNode; newNode->next=temp; return 1;//插入成功}int select_list(linkedList *list,int x)//查找数值 返回下标数组 不仅仅查找一个数值{ int i=1; linkedList *p=list->next;//遍历指针指向第一个,同上 //计数 j为arr的长度 while(p!=NULL) { if(p->data==x) return i;//找到返回下标 ++i; p=p->next; } //找不到返回-1 return -1; //拓展:查找的值不可能是唯一的,可以返回下标数组}int delete_list(linkedList *list,int pos){ linkedList *p=list; if(pos<1||list==NULL) return -1;//删除失败 int i=0; while(p!=NULL) { if(i==pos-1) break;//找到前驱结点 ++i; p=p->next; } if(i<pos-1) return -1;//查找失败,越界 linkedList *temp=p->next; p->next=p->next->next; free(temp); return 1;}int main(){ const int n=5; linkedList *mylist; mylist=(linkedList*)malloc(sizeof(linkedList));//先新建一个头节点 mylist->next=NULL; //大部分的函数我传参都是传的链表的地址 create_tail(mylist,n);//新建 traversing(mylist);//遍历 //插入操作 位置3 数值666 自己也可以输入 int flag=insert_node(mylist,3,666); if(flag!=-1) puts("插入成功,链表如下"); else puts("插入失败,链表如下"); traversing(mylist); //查找666的位置 肯定是3 因为上边刚插入的 //查找能做到了 修改也是easy的 printf("查找的位置%d\\n",select_list(mylist,666)); flag=delete_list(mylist,4);//删掉第4个 if(flag!=-1) puts("删除成功,链表如下"); else puts("删除失败,链表如下"); traversing(mylist); //拓展:将遍历链表改为求链表的长度,自己把头插法补充上吧 return 0;} 栈: 了解了基本性质就行了,c语言版的没必要,附上c++版的栈类,及常用方法 123456789101112131415161718192021222324252627282930313233#include <cstdio>#include <stack>using namespace std;//stack 为模板类 <> 里面用来放数据类型stack<char> mystack;int main(){ //empty 函数使用 返回bool值 空为true if(mystack.empty()) puts("栈是空的"); //push 入栈 pop 出栈 top 返回栈顶数值<>里面的类型 mystack.push('h');//1 mystack.push('e');//2 mystack.push('l');//3 mystack.push('l');//4 mystack.push('o');//5 while(!mystack.empty()) { char temp=mystack.top(); mystack.pop(); printf("%c ",temp); }//打印 o l l e h //size 即栈的大小 此时以及pop完毕 栈为空size为0 printf("\\n栈的大小为%d\\n",mystack.size()); return 0;} 福利:附上一个常用好玩东西 啥都能放的数组vector 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081#include <cstdio>#include <vector>#include <algorithm>//算法头文件using namespace std;//切记一定有 命名空间std//vector 为模板类 <> 里面用来放数据类型typedef pair<int,int> Point;/*对组 pair <type,type> 能放下一对数据,常用来表示坐标啊,对应关系啊pair.fist 顾名思义 第一个数据嘛 pair.second 表示第二个数据pair<int,int> 我把它定义成新的数据类型 坐标*/vector<Point> point_arr;/*先说一下构造函数 vector(int) 表示一个长为n数值全为0的数组啥也没有的话默认为空数组 size() 可以检测出来*/bool cmp(const Point a,const Point b);//这个先不用管,下文继续说int main(){ //push_back 什么back? 那有没有front? //对不起 没有 在一个线性数组里面往前插入一个数的时间复杂度是O(n) //插一个O(n) 插n个就是O(n^2) 效率可想而知 point_arr.push_back(Point(1,1)); point_arr.push_back(Point(1,-3)); point_arr.push_back(Point(2,4)); point_arr.push_back(Point(-12,8)); point_arr.push_back(Point(7,6));//在数组里面放入5个点 //我想按照y的大小从小到大 把这些点拍个序怎么排 //冒泡?太慢了 归并排序?自己又写不出来 //这时候 c++的好用之处就来了,看下一行代码 和最后一个函数 sort(point_arr.begin(),point_arr.end(),cmp); //begin 就相当于首指针 end 就相当于尾指针 两个相减就是数组大小 //不信的话可以试试,只不过stl把指针简化了,变安全了,改叫迭代器了 //cmp 就是上文的注释函数 即为排序函数,这个函数可以自己写,想怎么排就怎么排 //就是调用了系统的 快速排序函数 sort() //排序完了 就得打印了,是不是得pop啊 //这里可以不用pop 万一这些数据还用呢,pop了就没了 //这里可以用运算符重载 [] 直接用到下标,注意是否越界 puts("y从小到大的结果:"); for(unsigned int i=0;i<point_arr.size();++i) printf("(%d,%d)\\n",point_arr[i].first,//x point_arr[i].second//y ); //上面是用[] 可不可以用指针呢?可以,不过叫做迭代器 // iterator 这个类型的都叫迭代器 puts("遍历2:"); vector<Point>::iterator itor;//创建一个迭代器对象 for(itor=point_arr.begin();itor!=point_arr.end();itor++) { //注意指针的自增运算符 ++ 就是移动指针一个位置 printf("(%d,%d)\\n",itor->first,//x itor->second//y ); } //自己写一个按照x排序的吧 return 0;}bool cmp(const Point a,const Point b){ //按照y的大小排序 所以只看second 就行了 //a b 是有顺序的 第一个参数永远在第二个前面 if(a.second<b.second) return true;//后面比前面小就是true 从小到大 简单易懂 return false;}","categories":[],"tags":[{"name":"data-structure","slug":"data-structure","permalink":"https://LTitan.github.io/tags/data-structure/"}]},{"title":"操作系统--页面置换算法","slug":"页面置换算法","date":"2018-06-16T02:16:59.000Z","updated":"2018-09-25T09:46:40.000Z","comments":true,"path":"2018/06/16/页面置换算法/","link":"","permalink":"https://LTitan.github.io/2018/06/16/页面置换算法/","excerpt":"","text":"###最佳置换算法 本人只是简单的列举三种算法的解题过程,但是实际问题得具体分析,感谢刘静文学姐,对缺页率的计算加已改正。 假设系统给某进程分配了三个物理块,有以下的页面号引用串: 则前三次装入内存并未发生中断,但是缺页,如下: 第四次时,在页中未发现2,发生缺页中断,根据最佳置换算法,发生一下操作: 即舍弃内存页中引用串下次出现的最大值 == <7,0,1> -> <2,0,1> == 第五次时0存在,不会发生缺页中断 第六次3在内存页中未找到,缺页中断发生,置换: == <2,0,1> -> <2,0,3> == …… 依次类推,最后结果如下: <2,7,1> 缺页率为: 前三次未发生缺页中断,但是需要调入内存,仍属于缺页范围。 前三次加上红框缺页次数6 总次数17 f=(6+3)/17×100%=52.9% FIFO 算法 先进先出算法 还是以前的数据,有三个物理块,数据如下,且前三次不会发生缺页中断: 第四次时发生,缺页中断,先进先出算法:7先进的,所以7先出: == <7,0,1> -> <2,0,1> == 第5次时未发生缺页中断 第六次时,3未找到,发生缺页中断,如下: == <2,0,1> -> <2,3,1> == 依次类推,最终结果如下: <7,1,2> 缺页率计算如下: 前三次未发生缺页中断,但是需要调入内存,仍属于缺页范围。 前三次加上红框缺页次数10 总次数17 f=(10+3)/17×100%=76.5% 这个算法比上者算法接近多一倍 ##LRU算法(最近最久未使用) 还是如上数据,前三次结果未改变: 第四次时,和FIFO算法一致: == <7,0,1> -> <2,0,1> == 第五次未改变,第六次发生缺页中断,如下,最好比较FIFO和LRU的区别: == <2,0,1> -> <2,0,3> == 依次类推,最后结果如下: <1,7,2> 缺页率计算如下: 前三次未发生缺页中断,但是需要调入内存,仍属于缺页范围。 前三次加上红框缺页次数8 总次数17 f=(8+3)/17×100%=64.7%","categories":[],"tags":[{"name":"OS","slug":"OS","permalink":"https://LTitan.github.io/tags/OS/"}]},{"title":"进程同步问题","slug":"进程同步问题","date":"2018-06-13T02:16:59.000Z","updated":"2018-09-25T09:51:17.000Z","comments":true,"path":"2018/06/13/进程同步问题/","link":"","permalink":"https://LTitan.github.io/2018/06/13/进程同步问题/","excerpt":"","text":"#读写者问题(多个) 123456789101112131415161718192021222324252627282930313233343536373839404142int wmutex=1,rmutex=1;//读写信号量 int readCount=0;//读者数量 void Writer(); void Reader(); int main() { cobegin Reader(); Writer(); coend; } //写者: void Writer() { while(1){ P(wmutex); ... 写书 ... V(wmutex); } } //读者 void Reader() { while(1) { P(rmutex); if(readCount==0) P(wmutex); readCount++; V(rmutex); ... 读书 ... P(rmutex); readCount--; if(readCount==0) V(wmutex); V(rmutex); } } #哲学家就餐问题 123456789101112131415161718192021222324252627282930int chopstick[]={1,1,1,1,1};//五个筷子 int mutex=1; int main() { cobegin switch(i) { V(SB); case 1~5: Pi();break; } coend; } Pi()//第i个哲学家进餐 { while(1) { P(mutex); //在取筷子前获得互斥量 P(chopstick[i]); //取左边筷子 P(chopstick[(i+1)%5]); //取右边筷子 V(mutex); //释放取筷子的信号量 ... eat; //进餐 ... V(chopstick[i]); //放回左边筷子 V(chopstick[(i+1)%5]); //放回右边筷子 ... think;//思考 ... } } #生产者消费者问题 == 一个生产者 一个消费者 一个缓冲器:== 12345678910111213141516171819202122232425262728293031323334int buffer;//1 表示生产 0表示消费 int empty=1,full=0;//空和满 void producer(); void consumer(); int main() { cobegin producer(); consumer(); coend; } void producer() { while(1) { ... 生产 ... P(empty); buffer=1; V(full); } } void consumer() { while(1) { P(full); buffer=0; V(empty); ... 消费 ... } } 一个生产者 一个消费者 n个缓冲器: 123456789101112131415161718192021222324252627282930313233343536int buffer[1024],t=0,k=0; int empty=1,full=0; void producer(); void consumer(); int main() { cobegin producer(); consumer(); coend; } void producer() { while(1) { ... 生产 ... P(empty); buffer[k]=1; k=(++k)%n; V(full); } } void consumer() { while(1) { P(full); buffer[t]=0; t=(++t)%n; V(empty); ... 消费 ... } } #司机与售票员问题 1234567891011121314151617181920212223242526272829303132int s1=0,s2=0; void P1();//司机 void P2();//售票员 int main() { cobegin P1(); p2(); coend; } void P1() { while(1){ P(s1); ... 启动 正常行驶 到站 ... V(s2); } } void P2() { while(1) { 关门 V(s1); 售票 P(s2); 开门 } } #吃水果 问题描述 桌子上有一只盘子,最多可容纳两个水果,每次只能放人或取出一个水果。爸爸专向盘子中放苹果(apple),妈妈专向盘子中放桔子(orange),1个儿子专等吃盘子中的桔子,1个女儿专等吃盘子中的苹果。请用P、V操作来实现爸爸、妈妈、儿子、女儿之间的同步与互斥关系。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556int mutex=1; int empty=2; int apple=0,orange=0; void father(), mother(); void son(),daughter(); int main() { cobegin father(); mother(); son(); daughter(); coend; } void father() { while(1) { P(empty); P(mutex); put an apple; V(mutex); V(apple); } } void mother() { while(1) { P(empty); P(mutex); put an orange; V(mutex); V(orange); } } void son() { while(1) { P(orange); P(mutex); eat an oeange; V(mutex); V(empty); } } void daughter() { while(1) { P(apple); P(mutex); eat an apple; V(mutex); V(empty); } } #独木桥问题 某条河上只有一座独木桥,以便行人过河。现在河的两边都有人要过桥,按照下面的规则过桥。为了保证过桥安全,请用P、V操作分别实现正确的管理。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546int SA=1,SB=1,mutex=1; int countA=0,countB=0; void A(),B(); int main() { cobegin A(); B(); coend; } void A() { P(SA); if(countA==0) { P(mutex); countA++; } V(SA); 过独木桥; P(SA); countA--; if(countA==0) { V(metux); } V(SA); } void B() { P(SB); if(countB==0) { P(mutex); countB++; } V(SB); 过独木桥; P(SB); countB--; if(countB==0) { V(metux); } V(SB); }","categories":[],"tags":[{"name":"OS","slug":"OS","permalink":"https://LTitan.github.io/tags/OS/"}]},{"title":"OpenCV 蒙太奇效果","slug":"OpenCV 蒙太奇效果","date":"2018-06-08T03:30:22.000Z","updated":"2018-10-24T12:35:01.000Z","comments":true,"path":"2018/06/08/OpenCV 蒙太奇效果/","link":"","permalink":"https://LTitan.github.io/2018/06/08/OpenCV 蒙太奇效果/","excerpt":"","text":"展示一下图片的效果 原图 效果图 拼接图 代码区:注释即为思想 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374#include <sstream>#include <opencv2/opencv.hpp>#include <stdlib.h>#include <time.h>using namespace cv;using namespace std;int main(int argc, char *argv[]){ Mat src=imread(\"/media/lyf/doc/蒙太奇/change.jpeg\"); if(src.empty()) { return -1; } resize(src,src,Size(),0.5,0.5); //长宽能放下多少张小图片 int rows=src.rows,cols=src.cols; int much_row=rows/50,much_col=cols/50; much_col=much_col*50<cols?much_col+1:much_col; much_row=much_row*50<rows?much_row+1:much_row; Mat over=Mat(much_row*50,much_col*50,CV_8UC3,Scalar(255,255,255)); srand(time(NULL)); Mat micPic,temp; for(int i=0;i<much_row;++i) { for(int j=0;j<much_col;++j) { //读取拼接的图片 std::string filename=\"/media/lyf/doc/蒙太奇/m\"; std::stringstream ss; ss<<rand()%22+1; filename+=ss.str()+\".jpeg\"; //cout<<filename<<endl; micPic=imread(filename,CV_LOAD_IMAGE_UNCHANGED); if(micPic.empty()) { cout<<\"读取过程中出现错误\"<<endl; return -1; } //cout<<micPic.channels()<<endl; resize(micPic,micPic,Size(50,50)); temp=Mat(over,Rect(j*50,i*50,50,50)); micPic.copyTo(temp); micPic.release(); } } //将图片转为 float类型的便于比例的分割 Mat src_32f,over_32f; src.convertTo(src_32f,CV_32FC3); over.convertTo(over_32f,CV_32FC3); for(int i = 0; i < rows; ++i) { for(int j = 0; j < cols; ++j) { //黄金比列 over_32f.at<Vec3f>(i, j)[0] = saturate_cast<float>(0.382*over_32f.at<Vec3f>(i, j)[0]+0.618*src_32f.at<Vec3f>(i, j)[0]); over_32f.at<Vec3f>(i, j)[1] = saturate_cast<float>(0.382*over_32f.at<Vec3f>(i, j)[1]+0.618*src_32f.at<Vec3f>(i, j)[1]); over_32f.at<Vec3f>(i, j)[2] = saturate_cast<float>(0.382*over_32f.at<Vec3f>(i, j)[2]+0.618*src_32f.at<Vec3f>(i, j)[2]); } } //转为正常图片格式 uchar over_32f.convertTo(over,CV_8UC3); over=Mat (over,Rect(0,0,cols,rows)); imshow(\"原图\",src); imshow(\"生成的\",over); resize(over,over,Size(),1.5,1.5); imwrite(\"over.jpeg\",over); waitKey(0); return 0;}","categories":[],"tags":[{"name":"opencv","slug":"opencv","permalink":"https://LTitan.github.io/tags/opencv/"}]},{"title":"转移linux(一)qt+opencv3.3视频播放器加摄像头读取","slug":"转移linux(一)qt+opencv3.3视频播放器加摄像头读取","date":"2018-06-01T01:30:22.000Z","updated":"2018-10-24T12:40:07.000Z","comments":true,"path":"2018/06/01/转移linux(一)qt+opencv3.3视频播放器加摄像头读取/","link":"","permalink":"https://LTitan.github.io/2018/06/01/转移linux(一)qt+opencv3.3视频播放器加摄像头读取/","excerpt":"","text":"windows MFC c# 玩不了了 打算走嵌入式线路了 从opencv自己编译了n天,到今天第一个qt项目,差不多两周吧,自己电脑实体机装了**deep in linux ** 和 windows双系统,所以体验体验linux编程的乐趣 QT cteater的设计师界面效率快的不要不要的,更何况是c++,而且还吹嘘的什么跨平台。 附上开发的设计图 软件截图: 界面来讲还好吧,比mfc好看,主要视频还是用到QT自带的多线程来逐帧播放的,调试了5个小时,哈哈哈还是挺欣慰的,以后打算入手树梅派来做项目了。 就简单的附上视频的按钮以及核心代码吧 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596void MainWindow::on_openFileBtn_clicked(){ // 打开视频文件按钮 有播放的视频的话会先停止 isOpenFile=true; if(capture.isOpened()) { capture.release(); } //文件类型的对话框 不需要用指针新建一个类 //自行可以添加视频格式 QString FileName=QFileDialog::getOpenFileName(this,tr(\"打开文件\"),\".\", tr(\"Video Files(*.avi *.mp4 *.flv)\")); // capture.open(FileName.toLocal8Bit.data()); //防止字符串乱码转化 capture.open(FileName.toStdString());//字符串(全英文目录比较好)的转化 if(capture.isOpened()) { rate=capture.get(CV_CAP_PROP_FPS); if(isOpenFile) { vd_x=capture.get(CV_CAP_PROP_FRAME_COUNT); ui->videoLong->setRange(0,vd_x); } capture>>frame; if(!frame.empty()) { //自适应窗口大小 width_x=ui->ImageLabel->width()*1.0/frame.cols; height_x=ui->ImageLabel->height()*1.0/frame.rows; cv::resize(frame,frame,Size(),width_x,height_x); image=Mat2QImage(frame); //显示在label上 ui->ImageLabel->setPixmap(QPixmap::fromImage(image)); timer=new QTimer(this); //定时器线程 时间设置 相当与帧率 timer->setInterval(1000/rate); connect(timer,SIGNAL(timeout()),this,SLOT(nextFrame())); timer->start(); } } return;}QImage Mat2QImage(Mat cvImg){ //Mat 类型转 QImage 类似与Mat转bitmap QImage qImg; //判断通道个数 if(cvImg.channels()==3) { cvtColor(cvImg,cvImg,CV_BGR2RGB); qImg=QImage((const unsigned char *)(cvImg.data), cvImg.cols,cvImg.rows, cvImg.cols*cvImg.channels(), QImage::Format_RGB888 ); } else if(cvImg.channels()==1) { qImg=QImage((const unsigned char *)(cvImg.data), cvImg.cols,cvImg.rows, cvImg.cols*cvImg.channels(), QImage::Format_Indexed8 ); } else { qImg=QImage((const unsigned char *)(cvImg.data), cvImg.cols,cvImg.rows, cvImg.cols*cvImg.channels(), QImage::Format_RGB888 ); } return qImg;}void MainWindow::nextFrame(){ capture>>frame; if(!frame.empty()) { //调整大小 cv::resize(frame,frame,Size(),width_x,height_x); image=Mat2QImage(frame); ui->ImageLabel->setPixmap(QPixmap::fromImage(image)); if(isOpenFile) { ui->videoLong->setValue((int)(capture.get(CV_CAP_PROP_POS_FRAMES))); } }}","categories":[],"tags":[{"name":"opencv","slug":"opencv","permalink":"https://LTitan.github.io/tags/opencv/"}]},{"title":"图论--最短路径和最小生成树","slug":"图论--最短路径和最小生成树","date":"2018-04-25T09:30:22.000Z","updated":"2018-10-24T12:41:45.000Z","comments":true,"path":"2018/04/25/图论--最短路径和最小生成树/","link":"","permalink":"https://LTitan.github.io/2018/04/25/图论--最短路径和最小生成树/","excerpt":"","text":"######图的表示呢分为邻接矩阵和邻接链表。其主要算法层出不穷,这里主要介绍最短路径的ford和dijsktra,单源最短路径,思维可能有点局限,有什么好的想法可以联系我,代码有如雷同,不怨俺. (数组d[]表示求解的各点最短路径,from表示来源点,to表示目的点,cost表示权值,e(i,j)表示从i到j的边 ) 题目来源: 小木乃伊到我家 输入描述: 第一行输入两个整数n和m(2<=n<=m<=200000),分别表示有n座城市和m条路,城市编号为1~n(快递姐姐所在城市为1,AA所在城市为n)。 接下来m行,每行输入3个整数u,v,w(u,v<=n,w<=100000),分别表示城市u和城市v之间有一条长为w的路。 输出描述: 输出结果占一行,输出快递姐姐到达AA家最短需要走多远的路,如果没有路能走到AA家,则输出“qwb baka”(不用输出双引号)。 示例1 12345678输入4 41 2 12 3 23 4 32 3 1输出5 Ford 算法解决 递推公式呢就是 d[i]=min{d[j]+cost[i][j]} 但是这个效率比较低啊,时间O(e^2),这个算法程序就不详解了; 代码如下: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758#include <iostream>#include <algorithm>#include <cstdio>#include <cstdlib>using namespace std;#define MAX_E 200005#define INF 0x3f3f3f3f#include <time.h>struct edge{int from,to,cost;};edge es[MAX_E];//变 起点 终点 权值int d[MAX_E];//最短距离int V,E;//顶点数 边数void WithMax(){ for(int i=1;i<=V;i++) d[i]=INF;}void the_short_path(int s)//起点为s{ WithMax(); // fill(d,d+V,INF); d[s]=0;//起点的距离为0 while(1) { bool update=false; for(int i=1;i<=E;i++){ edge e=es[i]; if(d[e.from]!=INF&&d[e.to]>(d[e.from]+e.cost)) { d[e.to]=d[e.from]+e.cost; update=1; } } if(update==false) break; }}int main(){ while(cin>>V>>E) { clock_t start_time,end_time; start_time=clock(); for(int i=1;i<=E;i++) cin>>es[i].from>>es[i].to>>es[i].cost; the_short_path(1); if(d[V]==INF) cout<<\"qwbbaka\"<<endl; else cout<<d[V]<<endl; end_time=clock(); cout<<\"运行时间: \"<<(double)(end_time-start_time)/CLOCKS_PER_SEC<<endl; } return 0;} ####迪杰斯特拉算法 引入(楼主可能是太特么懒了): 上图所示的图,求0到各个点的最短距离 具体原理如下,最后填充的哪个点进集合的位置,就是加上之前的最小距离 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081#include <iostream>#include <cstdlib>#include <algorithm>#include <cstdio>#include <queue>#include <vector>using namespace std;#define MAX_A 200005#define INF 0x3f3f3f#define ll long long#include <time.h>struct node{ ll to,cost; node(){} node(ll x,ll y) { to=x; cost=y; } bool operator < (const node &a) const { return cost>a.cost; }};ll V,E;//顶点数和边数vector<node> G[MAX_A];ll vis[MAX_A];ll d[MAX_A];void dijkstra()//参数为起点{ //初始化 fill(vis,vis+V+1,0); fill(d,d+V+1,INF); d[1]=0; priority_queue<node> que; que.push(node(1,0)); while(!que.empty()) { node e=que.top(); que.pop(); if(vis[e.to]) continue; vis[e.to]=1; for(unsigned long i=0;i<G[e.to].size();i++) { ll to=G[e.to][i].to,cost=G[e.to][i].cost; if(!vis[to] && d[to]>d[e.to]+cost) { d[to]=d[e.to]+cost; que.push(node(to,d[to])); } }}}int main(){ ll f,t,c; while(cin>>V>>E) { clock_t start_t,end_t; start_t=clock(); for(ll i=1;i<=V;i++) G[i].clear(); for(ll i=0;i<E;i++) { cin>>f>>t>>c; G[f].push_back(node(t,c)); G[t].push_back(node(f,c)); } dijkstra(); cout<<d[V]<<endl; end_t=clock(); cout<<(double)(end_t-start_t)/CLOCKS_PER_SEC<<endl; }}","categories":[],"tags":[{"name":"data-structure","slug":"data-structure","permalink":"https://LTitan.github.io/tags/data-structure/"}]},{"title":"VS 自带数据库 MSSQLLocalDB 的使用方法","slug":"VS-自带数据库-MSSQLLocalDB-的使用方法","date":"2018-04-18T05:30:22.000Z","updated":"2018-10-24T12:39:02.000Z","comments":true,"path":"2018/04/18/VS-自带数据库-MSSQLLocalDB-的使用方法/","link":"","permalink":"https://LTitan.github.io/2018/04/18/VS-自带数据库-MSSQLLocalDB-的使用方法/","excerpt":"","text":"话说数据库开课了,机房用的是SQL server2008 r2,微软的这个东西有点大,但是已经有vs的童鞋有福利了,因为vs自带一个MSSqlLocalDatabase ,就是一个本地链接的数据库,我这里演示的是VS2015教程,2013、2017应该都可以的。 ##下面开始教程 #####首先打开vs 1、找到菜单栏,点击工具,找到连接SQL Server,如下图 2、然后会弹出这个窗口,选择本地,选择MsSqlLocalDB,然后连接,注意一定是windows验证才可以,如图 3、会在VS侧栏有个 SQl Server对象资源管理器,如下图,没找到这个窗口的,再看下个图 4、打开这个数据库,就是那个小箭头,添加新数据库,我这个例子呢是用的教材P71 的习题6,你们也可以换其他的测试 5、经常用的,起个新名字,放个路径 6、打开这个新建的数据库(小箭头),然后鼠标右键,新建查询 7、写你自己定义的SQL语句,然后执行脚本,最后刷新数据库,就会完成操作 8、最后给表添加数据,这里先不考虑SQL语言插入了,显示表之后,自己往上填就行了 除掉了烦人的sqlserver安装,还是挺省事吧。","categories":[],"tags":[{"name":"others","slug":"others","permalink":"https://LTitan.github.io/tags/others/"}]},{"title":"OpenCV 帧差法运动物体追踪","slug":"OpenCV-帧差法运动物体追踪","date":"2018-03-28T06:30:20.000Z","updated":"2018-10-24T12:32:55.000Z","comments":true,"path":"2018/03/28/OpenCV-帧差法运动物体追踪/","link":"","permalink":"https://LTitan.github.io/2018/03/28/OpenCV-帧差法运动物体追踪/","excerpt":"","text":"最近迷上了物体跟踪的玩意,于是乎找了好多视频去看,翻来覆去呢就是用到了轮廓检测的api,还有耐心的不断的调试。 原理呢是帧差法(就是一帧一帧的比较)非HSV分离 看看视频 软件的工作流程 您的浏览器不支持 video 标签。 代码呢,emmm,真的是不好理解。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172#include<opencv2/opencv.hpp>#include<opencv2/highgui/highgui.hpp>#include<opencv/cv.h>#include<opencv/highgui.h>using namespace std;using namespace cv;const static int SEAITVITV_VALUE = 20; //阈值操作的起始范围 可手动调试const static int BLUR_SIZE = 10;//blur 滤波Size大小int theObject[2] = { 0,0 };//发现目标的坐标 x,yRect objectBoundingRect = Rect(0, 0, 0, 0);//目标跳动的矩形string intTostring(int num){//int 类型 转为 stringstringstream ss;ss << num;return ss.str();}void searchForMovement(Mat thresgoldImage, Mat &cameraFeed){ //寻找移动物Mat temp;bool objectDetected;//形参的阈值图像传给中间变量thresgoldImage.copyTo(temp);//向量 用来存放轮廓点坐标 Pointvector < vector<Point> > contours;//向量 用来存放层次vector<Vec4i> hierarchy;//调用api 来获取层次和轮廓点findContours(temp, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);//判断轮廓点的个数是否为0if (contours.size() > 0) objectDetected = true;else { objectDetected = false; }if (objectDetected){ //找最大的轮廓点 vector<vector<Point> > largesContours; largesContours.push_back(contours.at(contours.size() - 1)); objectBoundingRect = boundingRect(largesContours.at(0)); //约找出接近中心位置的点 int xpos = objectBoundingRect.x + objectBoundingRect.width / 2; int ypos = objectBoundingRect.y + objectBoundingRect.width / 2; //传给全局变量暂放坐标 theObject[0] = xpos; theObject[1] = ypos;}int x = theObject[0];int y = theObject[1];//画一个圆圈circle(cameraFeed, Point(x, y), 20, Scalar(0, 255, 0), 2);//画十字架line(cameraFeed, Point(x, y), Point(x, y - 25),Scalar(0,255,0),2);line(cameraFeed, Point(x, y), Point(x, y + 25), Scalar(0, 255, 0), 2);line(cameraFeed, Point(x, y), Point(x-25, y ), Scalar(0, 255, 0), 2);line(cameraFeed, Point(x, y), Point(x+25, y ), Scalar(0, 255, 0), 2);//输出坐标putText(cameraFeed, \"tracking at:(\" + intTostring(x) + \",\" + intTostring(y) + \")\", Point(x, y), 1, 1, Scalar(0, 255, 0));}int main(){//按键是否调试bool debugMode = false;//按键是否跟踪bool trackingEnable = false;//是否暂停视频bool pause = false;//视频取帧的frame1 frame2Mat frame1, frame2;//上者灰度图像Mat gray1, gray2;//两者的不同图像Mat differenceImg;//视频类VideoCapture capture;//阈值后的图像Mat thres;while (1)//不断的播放视频 进行调试{ capture.open(\"3.mp4\"); if (!capture.isOpened()) { //打开视频失败 return -2; } //int total = capture.get(CV_CAP_PROP_FRAME_COUNT); //cout << total << endl; //获取当前帧 与总帧进行比较 从而循环 while (capture.get(CV_CAP_PROP_POS_FRAMES) < capture.get(CV_CAP_PROP_FRAME_COUNT) - 1) { //捕获frame1 capture.read(frame1); //转为灰度图 cvtColor(frame1, gray1, COLOR_BGR2GRAY); //捕获frame2 capture.read(frame2); //转为灰度图 cvtColor(frame2, gray2, COLOR_BGR2GRAY); //调用api 找出两者不同的区域 存入differenceImg absdiff(gray1, gray2, differenceImg); //阈值操作 threshold(differenceImg, thres, SEAITVITV_VALUE, 255, THRESH_BINARY); //滤波 去噪点 blur(thres, thres, Size(BLUR_SIZE, BLUR_SIZE)); //再进行一遍阈值 threshold(thres, thres, SEAITVITV_VALUE, 255, THRESH_BINARY); //是否进入调试模式 if (debugMode == true) { imshow(\"thres\", thres); } else { //销毁窗口 destroyWindow(\"thres\"); } //是否进入追踪捕获模式 if (trackingEnable) { searchForMovement( thres, frame1); } //显示原图像 imshow(\"frame\", frame1); switch (waitKey(50)) { case 27: return 0; case 't': case 'T': { trackingEnable = !trackingEnable; if (trackingEnable == false) cout << \"没有捕获\" << endl; else cout << \"捕获开始\" << endl; break; } case 'd': case 'D': { debugMode = !debugMode; if (debugMode == false) cout << \"调试模式退出\\n\"; else cout << \"进入调试模式\" << endl; break; } case 112: pause = !pause; if (pause == true) { cout << \"暂停\" << endl; while (pause == true) { switch (waitKey()) { case 112: pause = false; break; } } } else cout << \"开始\" << endl; break; } } //播放完后释放类 capture.release();}}","categories":[],"tags":[{"name":"opencv","slug":"opencv","permalink":"https://LTitan.github.io/tags/opencv/"}]},{"title":"新年? 当然是烟花了!!!","slug":"新年?-当然是烟花了!!!","date":"2018-02-26T03:34:20.000Z","updated":"2018-10-24T12:43:41.000Z","comments":true,"path":"2018/02/26/新年?-当然是烟花了!!!/","link":"","permalink":"https://LTitan.github.io/2018/02/26/新年?-当然是烟花了!!!/","excerpt":"","text":"###唉 又过年了 过年咱也送个礼物了,烟花绽放.exe,献给你们 内容呢是在b站上跟大佬学的,还是用到了简单的easyX 绘图库 文件和源码呢,就在上传的文件里面,解压就能看到了。 我感觉就是烟花障眼法","categories":[],"tags":[{"name":"others","slug":"others","permalink":"https://LTitan.github.io/tags/others/"}]},{"title":"图的基本遍历算法 DFS and BFS","slug":"图的基本遍历算法-DFS-and-BFS","date":"2018-01-24T03:34:20.000Z","updated":"2018-10-24T12:45:31.000Z","comments":true,"path":"2018/01/24/图的基本遍历算法-DFS-and-BFS/","link":"","permalink":"https://LTitan.github.io/2018/01/24/图的基本遍历算法-DFS-and-BFS/","excerpt":"","text":"先介绍下DFS 下面这张图呢是图的原来状态 这个gif呢是其深度优先遍历的过程,ppt做的,可能会失真 遍历的思想有了,但是对于非稀疏图来讲利用图的邻接矩阵遍历效果是不是更好。 那么代码就来了; 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667#include<iostream>#include<queue>#include<stack>/*图的深度优先搜索数据结构中介绍需要用到栈的帮助这里的栈用C++ STL的stack*/using namespace std;#define number 5//定义图的邻接矩阵void DFS(int start);int map[][5]={0,1,1,0,0,0,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0};//访问过的顶点int visited[number + 1];int main(){//初始化数组为0,表示一个也没访问memset(visited, 0, sizeof(visited));for (int i = 1; i <= number; i++){ if (visited[i] == 1) continue; DFS(i);}system("pause");return 0;}void DFS(int start){stack<int> stk;//顶点入栈stk.push(start);//表示访问visited[start] = 1;bool IS_PUSH = false;//是否有新的顶点入栈while (!stk.empty()){ IS_PUSH = false; int p = stk.top(); for (int i = 1; i <= number; i++) { if (map[p - 1][i - 1] == 1 && !visited[i]) //如果他俩联通,并且还没有访问过 { visited[i] = 1; stk.push(i); IS_PUSH = true;//新顶点入栈 break; } } if (!IS_PUSH) { cout << p << " "; stk.pop();//顶点出栈 }}} 运行结果,看图就能看出来了,不过代码只是更简单点了 BFS 代码用到队列 123456789101112131415161718192021222324void BFS(int start){queue<int> que;//顶点入队que.push(start);//表示访问visited[start] = 1;while (!que.empty()){ int p = que.front(); cout << p << " "; que.pop(); for (int i = 1; i <= number; i++) { if (map[p - 1][i - 1] == 1 && !visited[i]) //如果他俩联通,并且还没有访问过 { visited[i] = 1; que.push(i); } }}}","categories":[],"tags":[{"name":"data-structure","slug":"data-structure","permalink":"https://LTitan.github.io/tags/data-structure/"}]}]}