/   今日科技快讯   /

8月19日,三星电子公司发布财报显示,今年上半年,该公司销售额达到75.2万亿韩元(约合620亿美元),其中86%来自海外。韩国是亚洲第四大经济体(前三名是中国、日本、印度),同期出口总额为313.4万亿韩元(约合2596亿美元),其中海外收入占20.6%。

/   作者简介   /

本篇文章来自北斗星_And的投稿,分享了如何利用Android来给图片美颜,相信会对大家有所帮助!同时也感谢作者贡献的精彩文章。

北斗星_And的博客地址:

https://juejin.im/user/5d2ef4f7f265da1bb47d9a07

/   背景   /

最近刷抖音,看到一些大汉变“女神”,这化妆可以称之为逆袭啊,大汉变萝莉。作为技术,大部分是男生,并且经常有男生被女票怼我的口红有多少色号,那是一样的红色吗?为了广大男同胞能好好的“活在”女票跟前,今天来讲述一下化妆,用代码撸一个好看的女票。

/   开始   /

先上效果在说吧,学习抖音的化妆教程方式,就画一半,方便形成对比,效果如下:

如果正在看篇文章的人是个妹子,你应该很清楚画了些什么吧?为了照顾广大爷们,先讲一下画了些什么吧。直接看代码吧:

public enum Region {

FOUNDATION("粉底"),
    BLUSH("腮红"),
    LIP("唇彩"),
    BROW("眉毛"),

EYE_LASH("睫毛"),
    EYE_CONTACT("美瞳"),
    EYE_DOUBLE("双眼皮"),
    EYE_LINE("眼线"),
    EYE_SHADOW("眼影");

private String name;
    Region(String name) {
        this.name = name;
    }
}

女程序员们,你们看出这么多来了吗?其实我也是挺佩服我自己的,一个男生知道那么多,吓坏了我很多小伙伴,宅男的世界你们不懂。代码已经托管到github,如果你喜欢,请给一个star,谢谢!

https://github.com/DingProg/Makeup

/   磨皮   /

磨刀不误砍柴工,我们知道,一般的痘痘用粉底是盖不住的,那么先来一次磨皮吧,把"底板"搞干净了,我们使用一个高通滤波器(去掉低频信号,来达到保留细节的效果)+Curve Adjustment某些频率应用调整,然后在融合来达到磨皮的目的。流程大概是这样的。

效果如下:

本文就没有在撸一个这样的库,直接使用了github开源的磨皮库。使用HighPassSkinSmoothing,地址如下:

https://github.com/msoftware/HighPassSkinSmoothing-Android

但是我这里为了形成对比,所以只取了左边的脸。

Bitmap leftAndRightBitmap = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), Bitmap.Config.ARGB_8888);
 Canvas canvas = new Canvas(leftAndRightBitmap);
 //+3,为了弥补 int值相除精读损失,让左边多一些
Rect left = new Rect(0,0,bitmap.getWidth()/2 + 3,bitmap.getHeight());
Rect right = new Rect(bitmap.getWidth() - bitmap.getWidth()/2 ,0,bitmap.getWidth(),bitmap.getHeight());
  canvas.drawBitmap(result,left,left,null);
canvas.drawBitmap(bitmap,right,right,null);

/   人脸关键点检测   /

往人脸上化妆,拿整张照片的磨皮肯定不行啊,我们需要精准的人脸,那就需要人脸识别技术,开源的库也有一些,但是精度有待加强,所以本文选用了商用的人脸关键点检测技术,大概看了一下,有这么几家人脸识别技术做的还可以。

  • 商汤

  • Face++

  • 百度

  • 虹软

他们的技术,人脸精度,使用价格,在此不做评论。本文选用了Face++的稠密关键点检测。为了方便去见,没有下载其SDK,使用了网页版本的关键点检测,可以上传本地照片,然后把数据拿下来。

右侧有关键点的json,可以直接复制下来,供后续使用。

{
  "time_used": 140,
  "request_id": "1565152700,b5efc234-055c-4109-8899-e7bd0b9d1d63",
  "face": {
    "landmark": {
      "left_eye": {
        "left_eye_43": {
          "y": 170,
          "x": 140
        },
        "left_eye_42": {
          "y": 170,
          "x": 141
        },
        "left_eye_41": {
          "y": 170,
          "x": 142
        },
        "left_eye_40": {
          "y": 170,
          "x": 143
        },
        "left_eye_47": {
          "y": 170,
          "x": 136
        },
        "left_eye_46": {
          "y": 170,
          "x": 137
        }
       }
     }
   }
}

如果商用建议购买其SDK。有了这些点,我们就可以接下来“画”妆了。

/   粉底   /

有了磨皮,但是不够白啊,上述的库里其实包含了美白,它是对整个图片进行处理,叠加白色滤波,但效果很差,肯定不是我们想要的。但是有了人脸检测的点,那我们就好办了,涂一层粉底吧。(女生还要先涂水啊,乳啊什么,照片上不了水了....)

看Face++的文档我们可以知道json里面的关键点为face_contour_left_和face_hairline_为脸的区域。直接拿出左边脸的区域。

public static Path landmark(String faceJson){
        JSONObject jsonObject = null;
        try {
            jsonObject = new JSONObject(faceJson);
            JSONObject eye = jsonObject.getJSONObject("face").getJSONObject("landmark").getJSONObject("face");

Path path = new Path();
            Point start = getPointByJson(eye.getJSONObject("face_contour_left_0"));
            path.moveTo(start.x,start.y);
            for(int i= 1;i< 64;i++){
                Point point = getPointByJson(eye.getJSONObject("face_contour_left_"+i));
                path.lineTo(point.x,point.y);
            }

for(int i= 144;i>= 72;i--){
                Point point = getPointByJson(eye.getJSONObject("face_hairline_"+i));
                path.lineTo(point.x,point.y);
            }
            path.close();
            return  path;

} catch (JSONException e) {
            e.printStackTrace();
        }
        return null;

}

有了左边区域,只需要一个画笔就可以画上去(原图就可以是画板 new Canvas(originBitmap)),那我们正常直接涂一层白色,肯定不行,会吓坏小伙伴的,那白色加透明可以吗?那我们试试吧!

Canvas canvas = new Canvas(originBitmap);
 Paint paint = new Paint();
 paint.setColor(Color.WHITE);
 paint.setAlpha(50);
 paint.setStyle(Paint.Style.FILL);
 canvas.drawPath(facePath,paint);

效果如下所示:

感觉挺假的,我们知道,画笔是可以设置成高斯模糊的,那就来试试吧。

private static Bitmap createMask(final Path path, int color, @Nullable PointF position, int alpha, int blur_radius) {
        if (path == null || path.isEmpty())
            return null;

RectF bounds = new RectF();
        path.computeBounds(bounds, true);

int width = (int) bounds.width();
        int height = (int) bounds.height();
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);  // mutable
        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setMaskFilter(new BlurMaskFilter(blur_radius, BlurMaskFilter.Blur.NORMAL));
        paint.setColor(color);
        paint.setAlpha(alpha);
        paint.setStyle(Paint.Style.FILL);
        path.offset(-bounds.left, -bounds.top);
        canvas.drawPath(path, paint);
        if (position != null) {
            position.x = bounds.left;
            position.y = bounds.top;
        }
        return bitmap;
    }

事实证明这样是可以的,但是效果还是不咋行,那我们在用原图来做一次渐变,刚好可以达到效果。

private static Bitmap getGradientBitmapByXferomd(Bitmap originBitmap, float radius){
        if(radius < 10) radius = 10;
        Bitmap canvasBitmap = Bitmap.createBitmap(originBitmap.getWidth(),originBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(canvasBitmap);
        Paint paint = new Paint();

BitmapShader bitmapShader = new BitmapShader(originBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        RadialGradient radialGradient = new RadialGradient(originBitmap.getWidth() / 2, originBitmap.getHeight() / 2,
                radius, Color.BLACK, Color.TRANSPARENT, Shader.TileMode.CLAMP);
        paint.setShader(new ComposeShader(bitmapShader,radialGradient,new PorterDuffXfermode(PorterDuff.Mode.DST_IN)));
        canvas.drawRect(new Rect(0,0,canvasBitmap.getWidth(),canvasBitmap.getHeight()), paint);
        return canvasBitmap;
    }

/   口红   /

关于口红也只是仅仅画上一层颜色,有了画笔,就可以和粉底一样的实现方式。

先看一下怎么连接的区域吧,为了方便,我直接采用了把外面的区域连接起来,然后在去做一次diff就可以了,代码如下:

public static Path getMouthPath(String faceJson){
        try {
            JSONObject jsonObject = new JSONObject(faceJson);
            JSONObject mouthJson = jsonObject.getJSONObject("face").getJSONObject("landmark").getJSONObject("mouth");

Path outPath = new Path();
             Path inPath = new Path();

Point start = getPointByJson(mouthJson.getJSONObject("upper_lip_0"));
            outPath.moveTo(start.x,start.y);
             for(int i = 1;i < 18;i++){
                 Point pointByJson = getPointByJson(mouthJson.getJSONObject("upper_lip_" + i));
                 outPath.lineTo(pointByJson.x,pointByJson.y);
             }

for(int i = 16;i > 0;i--){
                Point pointByJson = getPointByJson(mouthJson.getJSONObject("lower_lip_" + i));
                outPath.lineTo(pointByJson.x,pointByJson.y);
            }
            outPath.close();

Point inStart = getPointByJson(mouthJson.getJSONObject("upper_lip_32"));
            inPath.moveTo(inStart.x,inStart.y);

for(int i = 46;i < 64;i++){
                Point pointByJson = getPointByJson(mouthJson.getJSONObject("upper_lip_" + i));
                inPath.lineTo(pointByJson.x,pointByJson.y);
            }

for(int i = 63;i >= 46;i--){
                Point pointByJson = getPointByJson(mouthJson.getJSONObject("lower_lip_" + i));
                inPath.lineTo(pointByJson.x,pointByJson.y);
            }

//取不同的地方
            outPath.op(inPath, Path.Op.DIFFERENCE);
            return  outPath;
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return null;
    }

Path.op()方法需要在API 19及以上才可以使用,如果使用了低版本的api,可以直接使用canvas.clipPath()。

/   腮红   /

只有粉底,那看上去,还是有点假,那是不是需要用画笔画上一个腮红呢?但是形状什么,不好搞定,所以选择了直接使用腮红素材,直接贴上去。

实现也相对容易一些。

public static void drawBlush(Canvas canvas, Bitmap faceBlush, Path path, int alpha) {
        Paint paint = new Paint();
        paint.setAlpha(alpha);
        RectF rectF = new RectF();
        path.computeBounds(rectF,true);
        canvas.drawBitmap(faceBlush,null,rectF,paint);

}

/   眉毛   /

眉毛这个其实困扰了我很长时间,因为要把底部的眉毛给扣了,在装新的眉毛在上面,不然可能完全盖不住,眉形变化,识别准确率,会导致效果的直接变化。尝试了很多方法其中OpenCV里有一个著名的inpaint方法的图片修复方法,看别人写的去书印demo,也都还行,但是放到这里去眉毛,效果很差,是因为我使用不对,还是什么问题,有大神可以指点,提取周边的皮肤颜色去掉原来的眉毛。

最终还是放弃了去掉原来的眉毛,直接覆盖眉毛。

public static Path getLeftEyeBrow(String faceJson){
        try {
            JSONObject jsonObject = new JSONObject(faceJson);
            JSONObject eye = jsonObject.getJSONObject("face").getJSONObject("landmark").getJSONObject("left_eyebrow");

Path path = new Path();
            Point start = getPointByJson(eye.getJSONObject("left_eyebrow_0"));
            path.moveTo(start.x,start.y);
            for(int i= 1;i< 64;i++){
                Point point = getPointByJson(eye.getJSONObject("left_eyebrow_"+i));
                path.lineTo(point.x,point.y);
            }
            path.close();
            return  path;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

public static void draw(Canvas canvas, Bitmap eyeBrowRes, Path path, int alpha){
        Paint paint = new Paint();
        paint.setAlpha(alpha);

RectF rectF = new RectF();
        path.computeBounds(rectF,true);

canvas.drawBitmap(eyeBrowRes,new Rect(0,0,eyeBrowRes.getWidth(),eyeBrowRes.getHeight() - 30),rectF,paint);
    }

最终效果如下所示:

但是文中的开始给的效果那张照片,因为识别偏差,导致效果不太好。

/   眼睛   /

眼睛(睫毛,眼影,双眼皮,眼线,美瞳),眼睛部分是最复杂的部分了,因为可以画的实在是太多了。这就将两个地方的实现,其他具体实现可以参考实际代码,先看一下这些不是主要的素材吧。

/   美瞳   /

要向眼睛里画美瞳,那么我们首先要有这个区域,区域人脸关键点已经给了,那么,我们知道,人的眼睛一般是椭圆性的,不可能直接是圆形的,所以画的时候,需要和眼睛的区域做一个交集来得到结果。

public static void drawContact(Canvas canvas, Bitmap contactBitmap, Path eyePath, Point centerPoint, int eyeRadius, int alpha) {
        Path contactPath = new Path();
        contactPath.addCircle(centerPoint.x,centerPoint.y,eyeRadius, Path.Direction.CCW);
        //重点地方,做交集得到结果
        contactPath.op(eyePath, Path.Op.INTERSECT);

RectF bounds = new RectF();
        contactPath.computeBounds(bounds,true);
        bounds.offset(1,0);
        Paint paint = new Paint();
        paint.setAlpha(alpha);
        canvas.drawBitmap(contactBitmap,new Rect(0,30,contactBitmap.getWidth(),contactBitmap.getHeight() - 60),bounds,paint);
    }

/   睫毛   /

我们知道,睫毛有上睫毛和下睫毛,那么怎么把这个眉毛画上去呢?其实我们知道,一般把图片绘制到目标区域需要经过,平移,旋转,缩放来进行。睫毛我们选取了素材上的三个点,和眼睛上的三个点来做上述的三个操作。

有了这三个点,我们就可以计算宽高比,角度,使用三角函数可以很容易计算得到。

/   旋转角度   /

使用人眼睛上对应的三个点来计算旋转角度,(如果人的头像是正的,可以不用计算,但是人可能偏头,什么,需要计算旋转角度,来warp)。

/**
     * @param p1 三角形顶点
     * @param p2 三角形顶点
     * @param p3 三角形顶点
     * @return 三角形顶点p3 到 p1,p3垂直高度
     */
    public double getTriangleHeight(Point p1, Point p2, Point p3) {
        int a = p1.x;
        int b = p1.y;
        int c = p2.x;
        int d = p2.y;
        int e = p3.x;
        int f = p3.y;
        //计算三角形面积
        double S = (a * d + b * e + c * f - a * f - b * c - d * e) / 2;
        int lengthSquare = (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y);
        return Math.abs(2 * S / Math.sqrt(lengthSquare));
    }

//获取坐标轴内两个点间的距离
    public double getLength(Point p1, Point p2) {
        double diff_x = Math.abs(p1.x - p2.x);
        double diff_y = Math.abs(p1.y - p2.y);
        //两个点在 横纵坐标的差值与两点间的直线 构成直角三角形。length_pow等于该距离的平方
        double length_pow = Math.pow(diff_x, 2) + Math.pow(diff_y, 2);
        double sqrt = Math.sqrt(length_pow);
        return sqrt == 0?0.001f:(float) sqrt;
    }

static double pi180 = 180 / Math.PI;
    public double getAngle(Point p1, Point p2, Point p3) {
        double _cos1 = getCos(p1, p2, p3);//第一个点为顶点的角的角度的余弦值
        return 90 - Math.acos(_cos1) * pi180;
    }

/   宽高比旋转角度   /

有了角度,那么我们在计算宽高比。

/**
     * @param targetP1 缩放目标线段点p1
     * @param targetP2 缩放目标线段点p2
     * @param P1       待缩放线段点p1
     * @param P2       待缩放线段点p2
     * @return 水平高度比值
     */
    public double computeScaleX(Point targetP1, Point targetP2, Point P1, Point P2) {
        int targetLengthSquare = (targetP1.x - targetP2.x) * (targetP1.x - targetP2.x) + (targetP1.y - targetP2.y) * (targetP1.y - targetP2.y);
        int sourceLengthSquare = (P1.x - P2.x) * (P1.x - P2.x) + (P1.y - P2.y) * (P1.y - P2.y);
        double scale = targetLengthSquare * 1.0 / sourceLengthSquare;
        return Math.sqrt(scale);
    }

/**
     * @param targetP1 缩放目标三角形顶点
     * @param targetP2 缩放目标三角形顶点
     * @param targetP3 缩放目标三角形顶点
     * @param P1       待缩放三角形顶点
     * @param P2       待缩放三角形顶点
     * @param P3       待缩放三角形顶点
     * @return 垂直高度比值
     */
    public double computeScaleY(Point targetP1, Point targetP2, Point targetP3, Point P1, Point P2, Point P3) {
        double targetHeight = getTriangleHeight(targetP1, targetP2, targetP3);
        double sourceHeight = getTriangleHeight(P1, P2, P3);
        return targetHeight / sourceHeight;
    }

/   平移   /

因为我们的图形是巨型,不可能从开始位置往上画,那就需要把画的位置通过平移,来达到第一个点的位置和对应位置的点,对应上。

eyeAngleAndScaleCalc.topP1.x - (int) (bean.topP1.x * eyeAngleAndScaleCalc.topScaleX),
                eyeAngleAndScaleCalc.topP1.y - (int) (bean.topP1.y * eyeAngleAndScaleCalc.topScaleY)

有了这些步骤,那既可以直接合成绘制了,代码如下:

public static void drawLash(Context context, Canvas canvas, EyeAngleAndScaleCalc.Bean bean, List<Point> pointList, int alpha, boolean needMirror) {
        EyeAngleAndScaleCalc eyeAngleAndScaleCalc = new EyeAngleAndScaleCalc(pointList,bean);

Paint paint = new Paint();
        paint.setAlpha(alpha);

Bitmap resTopBitmap = BitmapUtils.getBitmapByAssetsName(context,bean.resTop);
        Bitmap scaledBitmapTop = Bitmap.createScaledBitmap(resTopBitmap, (int) (resTopBitmap.getWidth() * eyeAngleAndScaleCalc.topScaleX + 0.5),
                (int) (resTopBitmap.getHeight() * eyeAngleAndScaleCalc.topScaleY + 0.5), true);
        resTopBitmap.recycle();

Bitmap resBottomBitmap = null;
        Bitmap scaledBitmapBottom = null;
        if (!TextUtils.isEmpty(bean.resBottom)) {
            resBottomBitmap = BitmapUtils.getBitmapByAssetsName(context,bean.resBottom);
            scaledBitmapBottom = Bitmap.createScaledBitmap(resBottomBitmap, (int) (resBottomBitmap.getWidth() * eyeAngleAndScaleCalc.bottomScaleX + 0.5),
                    (int) (resBottomBitmap.getHeight() * eyeAngleAndScaleCalc.bottomScaleY + 0.5), true);
            resBottomBitmap.recycle();
        }

if (needMirror) {
            Matrix matrix = new Matrix();
            matrix.postScale(-1, 1);   //镜像水平翻转
            scaledBitmapTop = Bitmap.createBitmap(scaledBitmapTop, 0, 0, scaledBitmapTop.getWidth(), scaledBitmapTop.getHeight(), matrix, true);
            if (resBottomBitmap != null) {
                scaledBitmapBottom = Bitmap.createBitmap(scaledBitmapBottom, 0, 0, scaledBitmapBottom.getWidth(), scaledBitmapBottom.getHeight(), matrix, true);
            }
        }

canvas.save();
        //canvas.rotate(eyeAngleAndScaleCalc.getTopEyeAngle(), eyeAngleAndScaleCalc.topP1.x, eyeAngleAndScaleCalc.topP1.y);
        canvas.drawBitmap(scaledBitmapTop,
                eyeAngleAndScaleCalc.topP1.x - (int) (bean.topP1.x * eyeAngleAndScaleCalc.topScaleX),
                eyeAngleAndScaleCalc.topP1.y - (int) (bean.topP1.y * eyeAngleAndScaleCalc.topScaleY), paint);
        canvas.restore();

if (scaledBitmapBottom != null) {
            canvas.save();
            canvas.rotate(eyeAngleAndScaleCalc.getBottomEyeAngle(), eyeAngleAndScaleCalc.bottomP1.x, eyeAngleAndScaleCalc.bottomP1.y);
            canvas.drawBitmap(scaledBitmapBottom, eyeAngleAndScaleCalc.bottomP1.x,
                    eyeAngleAndScaleCalc.bottomP1.y - (int) (bean.bottomP1.y * eyeAngleAndScaleCalc.bottomScaleY), paint);
            canvas.restore();
            scaledBitmapBottom.recycle();
        }
        scaledBitmapTop.recycle();
    }

眼睛部分,略微复杂一些,具体代码可以查看 Github Makeup ,如果你觉得还可以,可以给一个star吗?谢谢!

/   其他   /

我们知道,上述内容只是对脸上进行了一些化妆,那要成为真正的“美女”,可能还要打上问号?那什么样的化妆才是真正的美女呢,一般是底子好的人。在加上化妆就更漂亮了,那一张照片,要变的底子好,一般有那些方式呢?这里提供一些思路(包含美体):

public enum BeautyType {

INPAINT(1,"祛斑"),
    SMALLFACE(2,"瘦脸"),
    LONGLEG(3,"大长腿增高"),
    EYE(4,"眼睛放大"),
    BREST(5,"丰胸"),
    WHITE(7,"美白"),
    MAKEUP(8,"美妆"),
    SMALLBODY(9,"瘦脸瘦身");

private int type;
    private String name;

BeautyType(int type, String name) {
        this.type = type;
        this.name = name;
    }

public int getType() {
        return type;
    }

public String getName() {
        return name;
    }
}

如果只针对脸部,那么就只需要,磨皮,美白,祛斑,大眼,瘦脸等功能了。

/   文末   /

今天的文章分享到这就结束了,这些算法,目前知网论文库里都有,可以查看后轻松实现。

推荐阅读:

看一看Facebook工程师是怎么评价《第一行代码》的

全方位了解8.0系统下的Handler

给你的Android应用穿件花衣服吧!

欢迎关注我的公众号

学习技术或投稿

长按上图,识别图中二维码即可关注

开发一个App来为你的女神“化妆”!相关推荐

  1. mysql独立开发_独立开发一个 App 是一种怎样的体验?

    我要回答一下这样孤单的问题.哈哈哈. 我做过2个已经上线的APP. 第一个<仅仅电影>(已经下线了): 那时候年轻,想做一个独立的电影影单类的APP,推荐很多好看电影.然后想法立了就开始做 ...

  2. 倾心家教安卓案例开发代码_开发一个APP多少钱?

    从事互联网十余年,期间不少咨询这问过这个问题"开发一个APP多少钱?" 抠下字眼"开发"而不是购买,购买分多种方式,一种是买断,买的是源码(就是编写这个APP的 ...

  3. 用windows开发ios app_开发一个APP至少需要多少人|安卓|ios|产品经理

    现在APP充斥着我们的生活,我们的吃喝玩乐都离不开了它们.那一款APP的诞生最少需要几个人来完成呢?他们又都是做什么的呢? 1.产品经理 开发APP需要的不仅仅是技术人员,而且需要一个能够做需求分析的 ...

  4. 自己怎么开发一个软件app、如何开发一个app系统软件?

    自己怎么开发一个软件app.如何开发一个app系统软件? ​华盛恒辉开发app软件的办法如下: 1.华盛恒辉首先本人明白需求,懂代码,熟习开发流程. 2.华盛恒辉APP开发后期需求理解产品定位. 3. ...

  5. 「app开发价格」开发一个app到底需要多少钱

    1.一种是使用现成的模板进行修改:使用模板工作量较小,如果是一款功能简单,客户要求不太高的APP,只需要让美工对前台的页面进行一定程度的修改,最短一两天就能完成,费用最低2000元至3000元. 2. ...

  6. [APP资讯] 开发一个App要多少钱?有免费开发App的网站吗?

    试想,如果你是装潢公司老板,被问及:"装修一个房子要多少钱?",你会怎么回答?所以,当我被人问及:"开发一个App要多少钱?"时,我的感觉跟你一样一样的. 拿出 ...

  7. 开发一个app需要多少钱

    App应用开发是目前最热门的产业,很多企业都想通过app的开发来进入移动互联网市场分一杯羹. 那么你一定很想知道开发一个app需要多少钱吧?那下面企业帮就来帮大家计算一下费用吧. 面对app抄袭成风的 ...

  8. 为什么有必要开发一个APP?

    在这个大数据的时代,APP占据了很重要的地位,APP被广泛应用到各行各业,许多传统的企业看透了互联网的商机,纷纷着手开发属于自己独立的APP客户端.为何APP开发如此火热?有必要开发一个APP吗? 关 ...

  9. 开发一个app应用的流程有哪些

    想要知道开发一款app的难度,可以从app开发的流程来进行了解,这样就对于app开发有一个大款的认知,今天小编总结了一个app的开发到上线的过程,希望可以帮助到大家.要开发一个app,首先要做的就是需 ...

最新文章

  1. 关于MVC/P 的简单介绍
  2. Progressbar 实现从右向左 加载(逆向)
  3. 019_with语句
  4. open、read、write、文件类型
  5. MFC中给单文档程序添加背景图片
  6. vb.net利用SerialPort进行读取串口操作
  7. react组件之间传递信息/react组件之间值的传递
  8. linux之cp强制复制文件
  9. Google cloud 存储 Storage
  10. vibe的matlab实现,Matlab調用VIBE算法
  11. 【蚂蚁链学习2】蚂蚁链智能合约初级语法(数组、结构体、散列函数、事件)
  12. P2P模式文件传输网络应用的开发
  13. 【BZOJ 1233】 干草堆
  14. sort()sorted()
  15. oppoa57升级android版本,OPPO A57刷机教程_OPPO A57升级更新官方系统包
  16. StarUML 3.2.2
  17. java netty wss_netty中websocket, wss
  18. 【Android】问题总结:Aroute There`s no route matched
  19. mysql 分页及总数同事查询
  20. 纳尼?不用码代码,就可回归主流程,一只海豚就可以做到

热门文章

  1. Unity3d 动态字体
  2. 基于freeswitch1.6的IVR智能语音机器人交互逻辑lua脚本
  3. zeppelin安装配置
  4. 零基础自学新概念英语的方法
  5. java 经典算法(转)
  6. python+selenium之元素、下拉列表的定位
  7. 执行python generate_tfrecord.py 出现 utf-8‘ codec can‘t decode
  8. 网易AI工程师面试常见知识
  9. (附源码)springboot电影院售票与管理系统 毕业设计011449
  10. C语言实现矩阵的秩求解分析