$1 Unistroke Recognizer( lua )

来源:互联网 发布:护理优化服务流程 编辑:程序博客网 时间:2024/06/05 20:52

  最近在做一个游戏,里面涉及到了手势识别(比较复杂的手势),由于自身数学并不好。。去网上搜了一番,找到了这个$1识别算法,http://depts.washington.edu/aimgroup/proj/dollar/ 这是官网,发现这是目前网上比较好的一种手势识别算法,而且可以添加自定义手势模板。然后发现源码下载里面没有Lua的版本T_T,,,无奈之下,把js版本的大概翻译了一下,现在发上来好了。
  

local Recognizer = class("Recognizer")----- Point --local function Point(x,y)--    local pt = { X=0,Y=0 }--    pt.X = x--    pt.Y = y    return { X=x,Y=y }end----- Rectangle--local function Rectangle(x,y,width,height)    local rectangle = {}    rectangle.X = x    rectangle.Y = y    rectangle.Width = width    rectangle.Height = height    return rectangle end----- deglocal function Deg2Rad(d)    return (d * math.pi / 180.0)endlocal NumUnistrokes = 16;local NumPoints = 64;local SquareSize = 250.0;local Origin = Point(0,0);local Diagonal = math.sqrt(SquareSize * SquareSize + SquareSize * SquareSize);local HalfDiagonal = 0.5 * Diagonal;local AngleRange = Deg2Rad(45.0);local AnglePrecision = Deg2Rad(2.0);local Phi = 0.5 * (-1.0 + math.sqrt(5.0)) -- Golden Ratio----- distancelocal function Distance(p1, p2)    local dx = p2.X - p1.X;    local dy = p2.Y - p1.Y;    return math.sqrt(dx * dx + dy * dy);end----- pathLengthlocal function PathLength(points)    local d = 0.0;    for i = 2 ,#points,1 do        d = d + Distance(points[i - 1], points[i])    end    return dend----- pathDistancelocal function PathDistance(pts1, pts2)    local d = 0.0    for i = 1,#pts1,1 do -- assumes pts1.length == pts2.length        d = d + Distance(pts1[i], pts2[i])    end    return d / #pts1;end----- boundingboxlocal function BoundingBox(points)    local minX = math.huge    local maxX = -math.huge    local minY = math.huge    local maxY = -math.huge    for i = 1,#points, 1 do        minX = math.min(minX, points[i].X)        minY = math.min(minY, points[i].Y)        maxX = math.max(maxX, points[i].X)        maxY = math.max(maxY, points[i].Y)    end    return Rectangle(minX, minY, maxX - minX, maxY - minY)end----- centroid 矩心❤local function Centroid(points)    local x = 0.0    local y = 0.0    for i = 1,#points,1 do        x = x + points[i].X        y = y + points[i].Y    end    x = x / #points    y = y / #points    return Point(x, y)endlocal function RotateBy(points, radians) -- rotates points around centroid    local c = Centroid(points)    local cos = math.cos(radians)    local sin = math.sin(radians)    local newpoints = {}    for i = 1,#points,1 do        local qx = (points[i].X - c.X) * cos - (points[i].Y - c.Y) * sin + c.X        local qy = (points[i].X - c.X) * sin + (points[i].Y - c.Y) * cos + c.Y;        newpoints[#newpoints+1] = Point(qx, qy);    end    return newpoints;end-----distanceAtanglelocal function DistanceAtAngle(points, T, radians)    local newpoints = RotateBy(points, radians)    return PathDistance(newpoints, T.Points)end-----DistanceAtBestAnglelocal function DistanceAtBestAngle(points, T, a, b, threshold)    local x1 = Phi * a + (1.0 - Phi) * b;    local f1 = DistanceAtAngle(points, T, x1);    local x2 = (1.0 - Phi) * a + Phi * b;    local f2 = DistanceAtAngle(points, T, x2);    while math.abs(b - a) > threshold do        if (f1 < f2) then            b = x2;            x2 = x1;            f2 = f1;            x1 = Phi * a + (1.0 - Phi) * b;            f1 = DistanceAtAngle(points, T, x1);        else            a = x1;            x1 = x2;            f1 = f2;            x2 = (1.0 - Phi) * a + Phi * b;            f2 = DistanceAtAngle(points, T, x2);        end    end    return math.min(f1, f2);end-----OptimalCosineDistancelocal function OptimalCosineDistance(v1, v2) -- for Protractor    local a = 0.0;    local b = 0.0;    for i = 1,#v1,2 do--        print("           "..i.."              "..#v1.."     "..#v2)        a = a + v1[i] * v2[i] + v1[i + 1] * v2[i + 1]        b = b + v1[i] * v2[i + 1] - v1[i + 1] * v2[i]    end    local angle = math.atan(b / a)    return math.acos(a * math.cos(angle) + b * math.sin(angle));end-----Vectorizelocal function Vectorize(points) -- for Protractor    local sum = 0.0;    local vector ={}    for i = 1,#points,1 do        vector[#vector+1] = points[i].X;        vector[#vector+1] = points[i].Y;        sum = sum + points[i].X * points[i].X + points[i].Y * points[i].Y;    end    local magnitude = math.sqrt(sum);    for i = 1,#vector,1 do        vector[i] = vector[i] / magnitude;    end    return vector;end-----TranslateTolocal function TranslateTo(points, pt) -- translates points' centroid    local c = Centroid(points)    local newpoints = {}    for i = 1,#points,1 do        local qx = points[i].X + pt.X - c.X;        local qy = points[i].Y + pt.Y - c.Y;        newpoints[#newpoints+1] = Point(qx, qy);    end    return newpoints;end-----ScaleTolocal function ScaleTo(points, size) -- non-uniform scale; assumes 2D gestures (i.e., no lines)    local B = BoundingBox(points)    local newpoints = {}    for i = 1,#points,1 do        local qx = points[i].X * (size / B.Width)        local qy = points[i].Y * (size / B.Height)        newpoints[#newpoints+1] = Point(qx, qy);    end    return newpoints;end-----RotateBylocal function RotateBy(points, radians) -- rotates points around centroid    local c = Centroid(points);    local cos = math.cos(radians);    local sin = math.sin(radians);    local newpoints = {}    for i = 1,#points,1 do        local qx = (points[i].X - c.X) * cos - (points[i].Y - c.Y) * sin + c.X        local qy = (points[i].X - c.X) * sin + (points[i].Y - c.Y) * cos + c.Y        newpoints[#newpoints+1] = Point(qx, qy)    end    return newpoints;end-----IndicativeAnglelocal function IndicativeAngle(points)    local c = Centroid(points)    return math.atan2(c.Y - points[1].Y, c.X - points[1].X)end----- Private helper functions from this point down--local function Resample(points, n)    local I = PathLength(points) / (n - 1) -- interval length    local D = 0.0    local newpoints = { points[1] }--    print("           ijjhhhhhhh    local k = 2    while k<= #points do        local d = Distance(points[k - 1], points[k])--        print("         "..I)        if D + d >= I then--            print("         "..points[k - 1].X + ((I - D) / d) * (points[k].X - points[k - 1].X))             local qx = points[k - 1].X + ((I - D) / d) * (points[k].X - points[k - 1].X);            local qy = points[k - 1].Y + ((I - D) / d) * (points[k].Y - points[k - 1].Y);--            print("   "..points[k - 1].X.."   "..I.."   "..D.."   "..d.."   "..points[k].X.."  "..points[k - 1].X)             local q = Point(qx, qy)--            print("       "..q.X.."            "..q.Y.."            "..k-1)             newpoints[#newpoints+1] = q; -- append new point 'q'--            print("              "..#newpoints.."             "..k.."           "..#points)            table.insert(points,k,q)  --            points.splice(k, 0, q) -- insert 'q' at position k in points s.t. 'q' will be the next k            D = 0.0;        else            D = D + d;--            print("              "..#newpoints.."             "..k.."           "..#points)        end        k = k + 1    end    if #newpoints == n - 1 then -- somtimes we fall a rounding-error short of adding the last point, so add it if so        newpoints[#newpoints+1] = Point(points[#points].X, points[#points].Y)    end    return newpoints;end----- Unistroke class: a unistroke template--local function Unistroke(name, points) -- constructor    local uni = {}    uni.Name = name;    uni.Points = Resample(points, NumPoints);    local radians = IndicativeAngle(uni.Points);    uni.Points = RotateBy(uni.Points, -radians);    uni.Points = ScaleTo(uni.Points, SquareSize);    uni.Points = TranslateTo(uni.Points, Origin);    uni.Vector = Vectorize(uni.Points); -- for Protractor    return uniend----- Result class--local function Result(name, score) --constructor    local re = {}    re.Name = name;    re.Score = score;    return reendlocal tbSign = { "Heng" } --  手势的名称----- DollarRecognizer class--function Recognizer:DollarRecognizer() -- constructor    local mUnis = { Unistrokes={} }    --    -- one built-in unistroke per gesture type    --    mUnis.Unistrokes = {}    --模板    mUnis.Unistrokes[1] = Unistroke(tbSign[3],{ Point(80,304),Point(82,297),Point(87,281),Point(95,261),Point(102,236),Point(117,197),Point(128,173),Point(137,152),Point(140,146),Point(144,138),Point(145,134),Point(148,128),Point(149,126),Point(150,123),Point(151,121),Point(152,121),Point(153,117),Point(157,112),Point(159,109),Point(162,106),Point(162,105),Point(165,114),Point(173,131),Point(184,143),Point(197,164),Point(205,180),Point(213,200),Point(223,220),Point(228,242),Point(234,258),Point(238,271),Point(240,278),Point(243,285),Point(247,292),Point(251,297),Point(251,298),Point(251,298)});        --    -- The $1 Gesture Recognizer API begins here -- 3 methods: Recognize(), AddGesture(), and DeleteUserGestures()    ----    mUnis.Recognize = function(points, useProtractor)    function mUnis:Recognize(points,useProtractor)        local tbResult = {}        points = Resample(points, NumPoints);        local radians = IndicativeAngle(points);        points = RotateBy(points, -radians);        points = ScaleTo(points, SquareSize);        points = TranslateTo(points, Origin);        local vector = Vectorize(points); -- for Protractor        local b = math.huge        local u = -1        for i = 1,#mUnis.Unistrokes,1 do -- for each unistroke            local d;            if (useProtractor) then -- for Protractor                d = OptimalCosineDistance(mUnis.Unistrokes[i].Vector, vector);            else -- Golden Section Search (original $1)                d = DistanceAtBestAngle(points, mUnis.Unistrokes[i], -AngleRange, AngleRange, AnglePrecision)            end            if (d < b) then                b = d; -- best (least) distance                u = i; -- unistroke            end        end        if u == -1 then            return Result("No match.",0.0)        else            if useProtractor == true then                return Result( mUnis.Unistrokes[u].Name,1.0 / b )            else                return Result( mUnis.Unistrokes[u].Name,1.0 - b / HalfDiagonal )            end        end--        return (u == -1) ? new Result("No match.", 0.0) : new Result(this.Unistrokes[u].Name, useProtractor ? 1.0 / b : 1.0 - b / HalfDiagonal);    end----    this.AddGesture = function(name, points)--    function mUnis:AddGesture(name,points)--        mUnis.Unistrokes[#mUnis.Unistrokes+1] = Unistroke(name, points) -- append new unistroke--        local num = 0;--        for i = 1,#mUnis.Unistrokes,1 do--            if mUnis.Unistrokes[i].Name == name then--                num = num + 1--            end--        end--        return num;--    end----    this.DeleteUserGestures = function()--    function mUnis:DeleteUserGestures()----        #mUnis.Unistrokes = NumUnistrokes; -- clear any beyond the original set--        for i=#mUnis.Unistrokes ,NumUnistrokes,-1 do--            table.remove(mUnis.Unistrokes,i)--        end--        return NumUnistrokes;--    end    return mUnisendreturn Recognizer

ps:模板可以自定义,按照上面的格式添加进数组就可以,亲测可用。但是对于某些简单的手势识别概率不是太大,而且模板太多的话执行效率貌似也不高,求来个大大给优化一下。。

0 0
原创粉丝点击