2017年2月19日

線上共用白板AWWAPP複習生態系

前幾天要給九年級學生複習生態系,就想他們分組去整理各個生態系,突然靈機一動,想看看有什麼線上服務可以使用。

找到了一個可以線上共用的網路白板 AWWAPP,可以免費使用(也有付費版本)。


學生進入網站後,就會進入新的白板,如果要同組共用,就只要用Invite to Board,就可以秀出這個白板的連結還有QR CODE。因此我先讓組長先各自開一個白板,再把白板共用給組員們,而同時組長也把連結透過Google表單回傳給我,我就可以直接掌握各組繪圖的進度。

學生在這個課中活動中,每組分配一個生態系,每人要畫出兩種以上的生物,並且要寫出一種此生態系的特色。而組員合作要做的事情,就是要把生物之間的關係連線或是寫出來,還有畫出環境特色。

因為是線上共用的白板,所以同組內的繪圖彼此也都看得見進度,甚至要擦掉別人的畫面也行,因此組員之間的溝通非常重要。
IMAG3183 IMAG3174 IMAG3179


在學生平板上使用AWWAPP,是沒有辦法放大和縮小的,也就不知道全貌是怎樣的,不過平板上可以自由平移畫面。

如果是用電腦的話,就可以透過瀏覽器的縮小畫面,呈現出整個白板的繪圖狀況。


由於是透過網路連線在繪製,而上週學校連出的網路又有點問題,所以有發生A學生畫的畫面沒有呈現B學生的畫面。但如果網路都正常,這些問題就會減少很多。

免費版本的白板無法永久保存,沒有動作的情況,兩小時後會失效。所以我會把學生完成的白板再存圖下來,這有內建的[download image]可以使用。

學生製作完白板圖之後,我會再請學生報告圖中的初級消費者、次級消費者等。報告的模式,一種是用我電腦呈現的完整圖,另一種就是讓學生的平板透過air server投影到教師機,學生再利用平移畫面的方式來報告。

B3森林



6





5

3

2017年2月10日

用python爬蟲爬科教館的歷屆科展資料

科展群傑廳是科教館將歷屆科展作品集結起來的資料庫型網站,什麼時候建置的?我只能確定是2008年以後,因為在那之前,我還特別去把生物科展的資料撈出來做成一個網頁連結。(見此文章:來客全國生物科展總匯吧

科展群傑廳建置之後,搜尋資料應該都很順利了吧?是有比以前好,但是還是有很多可以改進的地方。

首先是伺服器的處理速度,我用Google的PageSpeed Tools對科展群傑廳的分類連接進行分析,伺服器要花近10秒的時間,才能載入頁面。預設每個搜尋結果都是呈現10筆資料,每次換下一頁,又要花上近10秒的時間。如果搜尋時選擇全網站搜尋(全國科展和國際科展都一起找),搜尋時間又會花上更久,例如我搜尋「水蘊草」,則要花21.5秒才能呈現第一頁的結果。

第二是顯示模式,搜尋結果預設是顯示摘要的前100字,但是許多科展都沒有把摘要寫好,可能前五十個字都是廢話,因此還得再一一點入,才能看到比較完整的摘要。

每次讓學生上去查東西,都要花上很久的時間,實在太折騰人了,所以我決定了!
就寫一個python爬蟲去把頁面呈現的資訊全部都爬下來,像是標題、作者、摘要、連結...那些。(code就放在本篇文章最底下,是給我自己看的)

我把全部的科展資料都爬下來了,共計9648筆資料,存成csv檔放在雲端硬碟上,你可以直接用搜尋的功能找到想要查的關鍵字。你也可以下載之後,用excel開啟後進行自動篩選或搜尋,速度都會比你在網站上找要快。當你找到要細看的文章時,最後一個欄位是檔案連結位置,可以直接點選或利用瀏覽器下載。

在雲端硬碟上的歷屆科展彙整連結  https://goo.gl/KKX3SX





以下是技術細節:

在科展群傑廳的資料庫裡,每一篇作品都會有一個sid變數(隨便點一篇科展的頁面,看網址列的最後一個變數就是)。所以我就是用sid從1去抓到最後一筆,目前sid用到最大是 13353,所以等到今年(2017)的科展作品也上線時,我就只要從13354繼續往上抓,就可以找到更新的資料了。


我使用的電腦環境是linux,所以我要搜尋科展資料的話,還可以用命令列來搜尋或是直接下載檔案。科展資料彙整的檔案,我存檔為data.csv

如果只是要搜尋關鍵字,可以用grep 去找關鍵字的科展作品
cat data.csv |grep "水蘊"

如果同時要找兩個關鍵字的交集,就只要把前一個關鍵字的搜尋結果,再用pipeline送給第二個grep
cat data.csv |grep "水蘊"|grep "聲音"

如果想要把搜尋出來的結果格式化成為比較容易閱讀的樣子,可把搜尋結果 pipeline到awk
cat data.csv |grep "水蘊"|awk -F"," '{print  "================\n",$2,"\t",$4,"\n================\n",$12,"\n\n" }
結果就會像這樣


搜尋關鍵字後想要一次下載全部有關的作品說明書,可以用awk 把wget的命令寫出來,再送給bash去執行。wget 的-O參數是指定下載的檔案名稱為科展作品的標題,所以這段小程式自動會將pdf檔改名為該科展的標題。
cat data.csv |grep "水蘊"|awk -F"," '{print "wget " $13 " -O " $2".pdf"}' |bash



python 3.0的code如下,之後使用上要特別修改的地方,就是用藍色標示的文字部份。
=====================

#!/usr/bin/env python
#-*- coding: utf-8 -*-

from bs4 import BeautifulSoup
from urllib.request import urlopen


def getData(url2):
    print(url2)
    file = open("data-6000.csv","a")  
    line = str(url2) + ","
   
    url1 = 'http://science.ntsec.edu.tw/Science-Content.aspx?sid='  
    url = url1+str(url2)
    page = urlopen(url)

    soup = BeautifulSoup(page, "html.parser")
   

    #如果網頁標題有Error,代表沒有這份資料
    if "Error" in soup.title.text :
        return

    #印出網頁編碼
    #print(soup.original_encoding)

    #印出科展標題
    for science_title1 in soup.findAll("div", { "class" : "science_title1" }):
        title = science_title1.contents[0]
        title = title.replace(" ", "")
        title = title.replace(",", ",")
        title = title.replace(";", ";")
        title = title.replace("\"", "")
        title = title.replace(r"\n", "")
        print("標題",title)
        line = line + title + ","
        #如果科展標題是評語有"評語",代表不是科展
        if "評語" in title :
            return

    #印出基本資料的欄位名稱和內容
    scienceContent1 = soup.findAll("li", { "class" : "scienceContent1" })
    scienceContent1s = soup.findAll("span", { "class" : "scienceContent1s" })
   
    #如果科展類別的字數小於3,代表無此資料,則跳出副程式
    if len(scienceContent1s[0].contents) == 0 :
        return


   
    for dataName,data in zip(scienceContent1,scienceContent1s):
        dataName = dataName.contents[0][0:-3]
        dataName = dataName.replace(" ", "")
        dataName = dataName.replace(" ", "")
       
        #處理沒有得獎的科展
        if len(data) == 0:
            data = ""
        else:
            data = data.contents[0]
            data = data.replace(" ", "")
            #data = data.replace(r"\r", "")
            data = data.replace(",", ",")
            data = data.replace(";", ";")
        print(dataName,data)
        line = line + data + ","
    #印出摘要
    #abstractName = soup.findAll("div", { "class" : "science_title2" })
   
    for abstract in soup.findAll("div", { "class" : "scienceContent2_font" }):
        abstract = abstract.text
        abstract = abstract.replace("\n", "")
        abstract = abstract.replace(r"\r", "")
        abstract = abstract.replace(",", ",")
        abstract = abstract.replace(";", ";")
        abstract = abstract.replace('^M', "")
       
        print("摘要",abstract)
        line = line + abstract + ","
    print("===")

    for link in soup.findAll("a", { "id" : "ctl00_cphMainContent_aDownload" }):
        #如果連結有正確的話,才加入連結
        if "href" in str(link):
            link = link['href']
            link = "http://science.ntsec.edu.tw/"+link
            print("連結",link)
            line = line + link
    line = line + "\n"
   
    file.write(line)
    file.close()
for url2 in range(5832,6001,1):
    getData(url2)


2017年2月1日

python+openCV電腦視覺即時分析車輛軌跡,偵測車速並拍照

去年11月寫了一篇《python+OpenCV做車輛追蹤》,分析定點攝影拍到的車流影片。不過之前的作法只有作到背景萃取出車輛的動態影像,對於車輛的計數是尚未作到的。

雖然openCV可以用cv2.findContours把影片中的每一幀中動態的部份分析出輪廓,得知每一幀中有多少移動的車輛,並且給予編號。但是同一輛車在前後幀的編號不一定相同的。

比如說第一幀有3輛車,分別給予編號123,到第二幀的時候,第一輛車已經消失在畫面,而有新的一輛車進入畫面。雖然前後兩幀的車輛數目都一樣,但是原本在第一幀的二號車,到第二幀時,就變成一號車了。

要讓電腦知道不同幀的車輛是屬於同一輛,可以利用一些演算法來做,像是knn、kmeans、meahshift、camshift、kalman filter等等。

不過我先用另外一種方式來做看看,因為我目的就是想把影片中的每一輛車擷取出來,所以我就在畫面中設定一個區域,只要車輛的輪廓進入這個區域,就把這個輪廓截圖出來。判定是否進入區域的方法,就是比較車輛的質心(或是其外框的一個角)是否在區域的範圍內。

這個區域的大小是一個重要因素,區域設得大,那麼同一輛車在不同幀的時候,可能都會落入這個區域,以至於同一輛車可能會被截圖二到三張。如果設得小,那麼某些開得快的車輛,可能會進不到這個區域(前一幀在區域的右邊,到下一幀已經到區域的左邊了)

雖然有這些缺點,但是還是可以滿足部份的需求,就至少可以把車輛截圖都抓取出來。以下就是某一分鐘內的車輛影像,白色或淺色的車輛佔比較高的比例呢。


python+OpenCV的車輛追蹤



後來我想到另一個方式讓電腦在不同幀中也能認出同一台車,原理就跟我們看車是類似的,如果給我們看兩張間隔一秒的靜態車流影像,我們怎麼認出兩張影像中的車輛是同一台呢?我們是藉由車輛的顏色、外型、位置去判斷的。

既然如此,那麼我就用位置來做吧。原理是把這一幀的車,跟上一幀的每一台車去比較位置,當位置差異最小而且在某一個範圍內,我就可以把不同幀的兩台車視作同一台車。

用程式實做出來,當然是可行,不過還是有一些問題。比方說一台車在某些幀的位置沒有被抓到(或是被抓歪了),那麼下一幀就沒辦法延續下去了。或是兩輛車在背景萃取的時候,因為太靠近,因此被視作同一部車,那麼到下一幀時,其中一部車也會無法延續下去。

以下影片就是實做出這樣的功能,藉由兩幀之間的位置變化,可以估算車速,也同時將車輛截圖下來,並且計算車輛數目。



雖然程式上的限制,會讓車輛數量被低估,但至少一些基本功能都已經有了。其實會想做這樣的事情,原因是十年前一個學生的科展需求。當初那學生的科展中有一部份要用到道路的車流量資料,那時她的作法就是週末花一些時間去站在路口算車子,當時我就很想看看是不是有其他方式可以達成目標,過了十年,總算是完成了(部份)。







定點攝影裡的頭前溪火災

前兩天一早看見窗外的頭前溪發生火災,河床上盡是煙霧和明火,不知道起火的原因是什麼,我推測是有人放鞭炮引起的吧。
DSC00647


DSC00648 DSC00645

野火燒到隔天,剩下一些灰燼,還有一些餘存的煙
DSC00650

擺一台定點攝影的攝影機好處就在這,可以用歷史紀錄去追溯出到底起火點在哪,
頭前溪野火起火點



也能夠將火災前後的畫面互相對比,看到燒過的範圍有什麼改變。可惜這部份的畫面太小,不然未來可以建立一系列連續的變化,觀察河床上的植被在火災後演替的變化情形。
頭前溪野火前後

2017年1月24日

段考進步怎麼看(2)-利用python製作學期成績PR變化折線圖

上次段考結束寫這篇《段考進步怎麼看》,這次段考結束,有了三次段考的成績,可以針對個人做出一些三次段考的PR變化,用這樣來了解每個學生這個學期的學習狀況。(再次強調,不可以光用原始成績來判斷成績進步與否,因為每次都是不同的測驗)


原始成績會受到每次測驗難易度而影響,而PR值是看自己在全校成績中的位置。

在考完國中第一次段考後,很多小孩剛上國中的家長心裡都會受打擊:「我小孩國小都考九十分,怎麼上國中只有考八十分或是更低」於是開始去尋覓補習班,幫小孩補習,然後到第二次段考發現成績有一些起色,就覺得補習真是有效。

可是啊,事情真的是這樣嗎?

這是最近三次段考的全校成績分佈圖,第二次段考成績比第一次高,不代表全校都用功了,可能是學生適應國中生活了,同時第二次也比較簡單。而第三次的低分成績多,是因為考的範圍變多了。
all 

學生的成績變化除了跟自身的努力有關,同時也跟該次評量的難易度有關。小學的原始成績是90分,但是如果拿全校成績分佈來看,那90分的PR值可能會只有80-90,因為更多人考100。所以光看原始成績來比較,就容易產生這種謬誤。


這次的圖表製作,是直接用python3寫幾十行程式去從資料裡撈出數據做折線圖,並且將個人圖表組成全班圖表,也方便我在段考結束後跟導師討論學生的學習狀況。畢竟導師還是要透過和任課老師討論才比較能知道學生在個別課的上課狀況。



馬賽克


code


=====以下是python3的code=====
# -*- coding: utf-8 -*-
#coding:utf-8

"""
Created on Wed Jan 18 21:41:17 2017

@author: pancala
"""
#讀取csv檔
import os
import pandas as pd
os.chdir("/media/pancala/3T/試題分析/105")
file='105A.csv'
data = pd.read_csv(file)



#定義中文,字型檔用  fc-list :lang=zh 查詢
from matplotlib.font_manager import *
myfont = FontProperties(fname='/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc')
#解決負號'-'顯示為方塊的問題
matplotlib.rcParams['axes.unicode_minus']=False



import numpy as np
import matplotlib.pyplot as plt


#把任教班級取出
for stuclass in [7,8,16,18]:
#for stuclass in [7]:
    #篩選出特定班級
    filter = data["班級"] == stuclass
    new_data = data[filter]
 
    #計算班級人數
    studentNum = new_data.shape[0]
 
    #subplot的列數由總班級人數決定
    subplotRowNum = int(np.ceil(studentNum/5))
 
    #設定subplots的列數、欄數和大小
    fig, axarr = plt.subplots(nrows=subplotRowNum,
                            ncols=5,
                            sharex=False,
                            sharey=False,
                            figsize=(10,subplotRowNum*2+2))
 
    #設定主標題
    fig.suptitle(str(stuclass)+ "班" +"上學期生物段考PR變化",fontproperties=myfont, size=20)  
    #plt.title(str(stuclass)+ "班" +"上學期生物段考PR變化",fontproperties=myfont, fontsize=10)
 
    #各小圖之間的間隔設定
    fig.tight_layout(pad=6, w_pad=1, h_pad=3.0)
 
    #把每一個學生的資料取出
    for i in range(0,studentNum):  
    #for i in [1]:
        #座號
        num = new_data.iloc[i,3]
        #座號前面補零
        if num <10:
            num=str(0)+str(num)
         
        #姓名
        name = new_data.iloc[i,4]
        #A1的PR
        a1 = new_data.iloc[i,8]
        a1 = float(a1)
        #A2的PR
        a2 = new_data.iloc[i,9]
        a2 = float(a2)
        #A3的PR
        a3 = new_data.iloc[i,10]
        a3 = float(a3)
   

        x= [1,2,3]
        y = [a1,a2,a3]
     

        #畫出PR折線圖
        axarr[int(i/5),i%5].plot(x,y)

        #限定y軸大小
        axarr[int(i/5),i%5].set_ylim([-5,105])

        #設定小圖的標題為姓名
        axarr[int(i/5),i%5].set_title(str(num) + name,fontproperties=myfont,size=15)
     
        #X軸標示設定為一二三
        axarr[int(i/5),i%5].set_xticks((1,2,3))
        axarr[int(i/5),i%5].set_xticklabels( ("一","二","三"),fontproperties=myfont)
        axarr[int(i/5),i%5].tick_params(labelsize=10)
     
        #plt.show()
 
    #班級前面補零
    if stuclass <10:
        stuclass=str(0)+str(stuclass)
    else:
        stuclass=str(stuclass)
     
     
    fig.savefig(stuclass + ".png",dpi = 250)
        #plt.close()
     

你可能對這有興趣

Related Posts Plugin for WordPress, Blogger...