ss3中常用的幾個動畫效果:
ease: cubic-bezier(0.25, 0.1, 0.25, 1.0)
linear: cubic-bezier(0.0, 0.0, 1.0, 1.0)
ease-in: cubic-bezier(0.42, 0, 1.0, 1.0)
ease-out: cubic-bezier(0, 0, 0.58, 1.0)
ease-in-out: cubic-bezier(0.42, 0, 0.58, 1.0)
下面上圖看效果:
網站截圖
效果
效果
效果
在線預覽網站:http://yisibl.github.io/cubic-bezier/#0,0,1,1
如果想交流的可以打開下面的網站:http://www.mackxin.com/xininn.html
一起學習,一起交流(前端,PS,分享)
關注分享,體驗樂趣
分享是一種態度
import numpy as np import argparse import time import cv2 # construct the argument parse and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-v", "--video", help = "path to the (optional) video file") args = vars(ap.parse_args()) # define the upper and lower boundaries for a color # to be considered "blue" blueLower = np.array([100,67,0],dtype="uint8") blueUpper = np.array([255,128,50],dtype="uint8") # load the video if not args.get("video"): camera = cv2.VideoCapture(0) else: camera = cv2.VideoCapture(args["video"])
們將使用NumPy進行數值處理,使用argparse解析命令行參數,使用cv2進行OpenCV綁定。time包是可選的。
我們只需要一個命令行參數,--video,也就是我們視頻的路徑。
我們將在視頻中追蹤的對象是藍色物體。由于除了該物體外,藍色在視頻中的任何其他位置都不常見,因此我們希望跟蹤藍色陰影。為了完成這種顏色跟蹤,我們定義了藍色陰影的下限和上限。請記住,OpenCV表示RGB顏色空間中的像素,但順序相反。
在這種情況下,如果大于R=0,G=67,B=100且小于R=50,G=128,B=255,則將顏色定義為“藍色”。
最后,我們打開視頻文件并使用cv2.VideoCapture函數獲取對它的引用。我們將此引用賦值給變量camera。
# keep looping while True: # grab the current frame (grabbed,frame) = camera.read() # check to see if we have reached the end of the video if args.get("video") and not grabbed: break # determine which pixels fall within the blue boundaries # and then blur the binary image blue = cv2.inRange(frame,blueLower,blueUpper) blue = cv2.GaussianBlur(blue,(3,3),0)
現在我們有了對視頻的引用,便可以開始處理幀。
我們開始循環遍歷幀,一次一個。調用read()方法的調用抓取視頻中的下一幀,返回具有兩個值的元組。第一個是grabbed,是一個布爾值,表示是否從視頻文件中成功讀取了幀。第二個frame,是幀本身。
然后,我們檢查frame是否成功讀取。如果未讀取框架,則表示已到達視頻的末尾,我們break掉while循環。
為了在frame中找到藍色陰影,我們必須使用cv2.inRange函數。該函數有三個參數。第一個是我們想要檢查的frame。第二個是RGB像素的lower threshold,第三個是上限閾值(upper threshold)。調用此函數的結果是閾值圖像,像素落在上下范圍內設置為白色,像素不屬于此范圍 設為黑色。
最后,我們對閾值圖像進行高斯模糊處理,以使查找輪廓更準確。
# find contours in the image (_,cnts,_) = cv2.findContours(blue.copy(),cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # check to see if any contours were found if len(cnts) > 0: # sort the contours and find the largest one -- # we will assume this contour coorespondes to the # area of my phone cnt = sorted(cnts,key=cv2.contourArea,reverse=True)[0] # compute the (rotated) bounding box around then # contour and then draw it rect = np.int32(cv2.boxPoints(cv2.minAreaRect(cnt))) cv2.drawContours(frame,[rect],-1,(0,255,0),2) # show the frame and the binary image cv2.imshow("Traccking",frame) cv2.imshow("Binary",blue) # if your machine is fast, it may display the frames in # what appears to be 'fast forward' since more than 32 # frames per second are being displayed -- a simple hack # is just to sleep for a tiny bit in between frames; # however, if your computer is slow, you probably want to # comment out this line time.sleep(0.025) # if the 'q' key is pressed, stop the loop if cv2.waitKey(1) & 0xFF == ord("q"): break # cleanup the camera and close any open windows camera.release() cv2.destroyAllWindows()
現在我們有了閾值圖像,那么我們需要找到圖像中最大的輪廓,假設最大輪廓對應于我們想要跟蹤的藍色物體輪廓。
我們調用cv2.findContours會在閾值圖像中找到輪廓。我們使用copy()方法克隆閾值圖像,因為cv2.findContour函數對傳入的NumPy數組具有破壞性。
然后檢查以確保實際發現輪廓。如果輪廓列表的長度為零,則沒有找到藍色區域。如果輪廓列表的長度大于零,那么我們需要找到最大的輪廓。這里,輪廓按相反的順序排序(最大的第一個),使用cv2.contourArea函數來 計算輪廓的面積。具有較大區域的輪廓存儲在列表的前面。在這種情況下,抓住具有最大面積的輪廓,再次假設該輪廓對應于藍色物體的輪廓。
現在我們有了藍色的輪廓,但我們需要在它周圍繪制一個邊界框。
調用cv2.minAreaRect計算輪廓周圍的最小邊界框。然后,cv2.boxPoints將邊界框重新定義為點列表。
注意:在OpenCV 2.4.X中,我們將使用cv2.BoxPoints函數來計算輪廓的旋轉邊界框。但是,在OpenCV 3.0+中,此函數已移至cv2.boxPoints。兩個函數執行相同的任務,只是略有不同的命名空間。
最后,我們使用cv2.drawContours函數繪制邊界框。
具有檢測到的藍色物體的frame顯示在第一個imshow,并且閾值圖像(落入藍色像素的下/上范圍的像素)顯示在第二個imshow。
上面,time.sleep(0.025)可選的。在許多較新型號的機器上,系統可能足夠快以處理>32幀/秒。如果是這種情況,找到可接受的睡眠時間將減慢處理速度并將其降低到更正常的速度。
執行我們的腳本
python track.py
結果:
或者指定視頻路徑
python track.py --video "video\2018-11-27 18-38-15-927.mp4"
也是可以的。
<script src="https://lf3-cdn-tos.bytescm.com/obj/cdn-static-resource/tt_player/tt.player.js?v=20160723"></script>
博客地址(有完整代碼):https://0leo0.github.io/2018/case_study_03.html
關注不迷路哦??!
以上兩種實現方式我們可以根據兩個因素 來決定哪一種更加適合自己:節點數量 的多少、排布的密集度 。
前者適合節點數量較少,排布比較稀松,例如,出租車軌跡回放,出租車行駛速度快,周期上報的時間也會相對較長。后者更加針對節點數量巨大、排布密集的路徑,按秒記錄位置的飛機行進軌跡,精細的地理邊界等等。
無論選擇兩種方式,我們都需要先收集到客戶端上報的信息,這些信息可以自定義,通常我們會包含:經緯度、速度、逆編碼之后的地理位置、方向、海拔 等基本地理信息,同時我們也可以加入一些自定義 的信息,例如:人員信息(頭像昵稱等)、出行信息(訂單等)。
實現的流程:
客戶端上報的數據是按時間周期上報的,也就是說每個時間都對應了一個經緯度,經緯度在地圖上就是一個又一個點,將這些點連接時,我們會得到 N 多條折線,為了繪制的軌跡更加美觀,行動路線更加明確平滑,通常我們需要一個算法來簡化折線。
例如:
這里官方也推薦了一種算法庫 simplify.js供大家參考,這里不做過多的闡述。
這里我們使用第一種方式來實現 - 利用 JS API 和 AMap.PolyLine。
我們可以讓車輛:
AMap.plugin('AMap.MoveAnimation', function(){
var marker, lineArr = [[116.478935,39.997761],[116.478939,39.997825],[116.478912,39.998549],[116.478912,39.998549],[116.478998,39.998555],[116.478998,39.998555],[116.479282,39.99856],[116.479658,39.998528],[116.480151,39.998453],[116.480784,39.998302],[116.480784,39.998302],[116.481149,39.998184],[116.481573,39.997997],[116.481863,39.997846],[116.482072,39.997718],[116.482362,39.997718],[116.483633,39.998935],[116.48367,39.998968],[116.484648,39.999861]];
var map = new AMap.Map("container", {
resizeEnable: true,
center: [116.397428, 39.90923],
zoom: 17
});
marker = new AMap.Marker({
map: map,
position: [116.478935,39.997761],
icon: "https://a.amap.com/jsapi_demos/static/demo-center-v2/car.png",
offset: new AMap.Pixel(-13, -26),
});
// 繪制歷史軌跡
var polyline = new AMap.Polyline({
map: map,
path: lineArr,
showDir:true,
strokeColor: "#28F", //線顏色
// strokeOpacity: 1, //線透明度
strokeWeight: 6, //線寬
// strokeStyle: "solid" //線樣式
});
// 駕駛途徑過的軌跡
var passedPolyline = new AMap.Polyline({
map: map,
strokeColor: "#AF5", //線顏色
strokeWeight: 6, //線寬
});
// 監聽車輛移動事件
marker.on('moving', function (e) {
// 延長駕駛途徑過的軌跡
passedPolyline.setPath(e.passedPath);
// 將車輛位置設置為地圖中心點
map.setCenter(e.target.getPosition(),true)
});
map.setFitView();
// 開始移動
window.startAnimation = function startAnimation () {
marker.moveAlong(lineArr, {
// 每一段的時長
duration: 500,//可根據實際采集時間間隔設置
// JSAPI2.0 是否延道路自動設置角度在 moveAlong 里設置
autoRotation: true,
});
};
// 暫停移動
window.pauseAnimation = function () {
marker.pauseMove();
};
// 恢復移動
window.resumeAnimation = function () {
marker.resumeMove();
};
// 停止移動
window.stopAnimation = function () {
marker.stopMove();
};
});
參考鏈接:https://lbs.amap.com/demo/jsapi-v2/example/marker/replaying-historical-running-data
使用 JS API 和 AMapUI 組件庫 配合使用,利用 PathSimplifier(軌跡展示組件)繪制出行動軌跡,這種方案比較簡單,只需要進行一些配置即可,例如說方案一中的倍速播放就需要計算,同時還存在不能動態改變倍速的弊端,但是方案二卻不會存在。
//加載PathSimplifier,loadUI的路徑參數為模塊名中 'ui/' 之后的部分
AMapUI.load(['ui/misc/PathSimplifier'], function(PathSimplifier) {
if (!PathSimplifier.supportCanvas) {
alert('當前環境不支持 Canvas!');
return;
}
//啟動頁面
initPage(PathSimplifier);
});
function initPage(PathSimplifier) {
//創建組件實例
var pathSimplifierIns = new PathSimplifier({
zIndex: 100,
map: map, //所屬的地圖實例
getPath: function(pathData, pathIndex) {
//返回軌跡數據中的節點坐標信息,[AMap.LngLat, AMap.LngLat...] 或者 [[lng|number,lat|number],...]
return pathData.path;
},
getHoverTitle: function(pathData, pathIndex, pointIndex) {
//返回鼠標懸停時顯示的信息
if (pointIndex >= 0) {
//鼠標懸停在某個軌跡節點上
return pathData.name + ',點:' + pointIndex + '/' + pathData.path.length;
}
//鼠標懸停在節點之間的連線上
return pathData.name + ',點數量' + pathData.path.length;
},
renderOptions: {
//軌跡線的樣式
pathLineStyle: {
strokeStyle: 'red',
lineWidth: 6,
dirArrowStyle: true
}
}
});
//這里構建兩條簡單的軌跡,僅作示例
pathSimplifierIns.setData([{
name: '軌跡0',
path: [
[100.340417, 27.376994],
[108.426354, 37.827452],
[113.392174, 31.208439],
[124.905846, 42.232876]
]
}, {
name: '大地線',
//創建一條包括500個插值點的大地線
path: PathSimplifier.getGeodesicPath([116.405289, 39.904987], [87.61792, 43.793308], 500)
}]);
//創建一個巡航器
var navg0 = pathSimplifierIns.createPathNavigator(0, //關聯第1條軌跡
{
loop: true, //循環播放
speed: 1000000
});
navg0.start();
}
參考鏈接:https://lbs.amap.com/demo/amap-ui/demos/amap-ui-pathsimplifier/index
*請認真填寫需求信息,我們會在24小時內與您取得聯系。