博客
关于我
Android素描算法及实现手指在图片上左右滑动调节图片透明度,最终实现类似调节素描浓度的效果
阅读量:373 次
发布时间:2019-03-05

本文共 13876 字,大约阅读时间需要 46 分钟。

Android图像处理:将照片转化为素描的实现方法

一、前期基础知识储备

UI设计师掌握Adobe Photoshop这一强大工具,可以轻松实现将实拍照片转化为素描或水彩画风格的效果。本文将详细介绍实现这一过程的步骤,并提供对应的Android代码示例。

转化为素描的关键步骤

  • 去色,获取黑白图

    在Photoshop中,按下快捷键Ctrl+Shift+U可以将图片转换为黑白颜色。每个像素的值将通过公式grey = (int)(r * KR + g * KG + b * KB)计算,最后将灰度值转换为16 bits颜色值。

  • 反相,得到底片效果

    使用reverseColor方法,将每个像素取反,模拟照片底片的效果。

  • 高斯模糊,得到反高斯图像

    使用高斯模糊算法,平滑像素值,得到反高斯效果的中间结果。

  • 颜色减淡,生成素描图

    最后通过colorDodge方法,结合反相和高斯模糊的结果,实现颜色的减淡效果。

  • 二、上代码,具体实现素描算法

    1. 去色,获取黑白图

    public static int[] discolor(Bitmap bitmap) {    int picHeight = bitmap.getHeight();    int picWidth = bitmap.getWidth();    int[] pixels = new int[picWidth * picHeight];    bitmap.getPixels(pixels, 0, picWidth, 0, 0, picWidth, picHeight);    for (int i = 0; i < picHeight; ++i) {        for (int j = 0; j < picWidth; ++j) {            int index = i * picWidth + j;            int color = pixels[index];            int r = (color & 0x00ff0000) >> 16;            int g = (color & 0x0000ff00) >> 8;            int b = color & 0x000000ff;            int grey = (int) (r * 0.393 + g * 0.769 + b * 0.234);            pixels[index] = grey < 16 ? (grey < 8 ? grey : grey | 0x00ff0000) : grey | 0xff000000;        }    }    return pixels;}

    2. 反相,得到底图

    public static int[] reverseColor(int[] pixels) {    int length = pixels.length;    int[] result = new int[length];    for (int i = 0; i < length; ++i) {        int color = pixels[i];        int r = 255 - ((color & 0x00ff0000) >> 16);        int g = 255 - ((color & 0x0000ff00) >> 8);        int b = 255 - (color & 0x000000ff);        result[i] = (r < 16 ? (r < 8 ? r : r | 0x00ff0000) : r | 0xff000000)                | (g < 16 ? (g < 8 ? g : g | 0x0000ff00) : g | 0x00ff0000)                | (b < 16 ? (b < 8 ? b : b | 0x000000ff) : b | 0x00000000);    }    return result;}

    3. 高斯模糊,反高斯处理

    public static void gaussBlur(int[] data, int width, int height, int radius, float sigma) {    float pa = 1.0f / (Math.sqrt(2 * Math.PI) * sigma);    float pb = -1.0f / (2 * sigma * sigma);    float[] gaussMatrix = new float[radius * 2 + 1];    float gaussSum = 0.0f;    for (int i = 0, x = -radius; x <= radius; ++i, ++x) {        float g = pa * Math.exp(pb * x * x);        gaussMatrix[i] = g;        gaussSum += g;    }    for (int i = 0, length = gaussMatrix.length; i < length; ++i) {        gaussMatrix[i] /= gaussSum;    }    for (int y = 0; y < height; ++y) {        for (int x = 0; x < width; ++x) {            float r = 0.0f, g = 0.0f, b = 0.0f;            float gaussSum = 0.0f;            for (int j = -radius; j <= radius; ++j) {                int k = x + j;                if (k >= 0 && k < width) {                    int index = y * width + k;                    int color = data[index];                    int cr = (color & 0x00ff0000) >> 16;                    int cg = (color & 0x0000ff00) >> 8;                    int cb = color & 0x000000ff;                    r += cr * gaussMatrix[j + radius];                    g += cg * gaussMatrix[j + radius];                    b += cb * gaussMatrix[j + radius];                    gaussSum += gaussMatrix[j + radius];                }            }            int index = y * width + x;            float avgR = r / gaussSum;            float avgG = g / gaussSum;            float avgB = b / gaussSum;            data[index] = (int)(avgR < 16 ? (avgR < 8 ? avgR : avgR | 0x00ff0000) : avgR | 0xff000000)                    | (int)(avgG < 16 ? (avgG < 8 ? avgG : avgG | 0x0000ff00) : avgG | 0x00ff0000)                    | (int)(avgB < 16 ? (avgB < 8 ? avgB : avgB | 0x000000ff) : avgB | 0x00000000);        }    }    for (int x = 0; x < width; ++x) {        for (int y = 0; y < height; ++y) {            float r = 0.0f, g = 0.0f, b = 0.0f;            float gaussSum = 0.0f;            for (int j = -radius; j <= radius; ++j) {                int k = y + j;                if (k >= 0 && k < height) {                    int index = k * width + x;                    int color = data[index];                    int cr = (color & 0x00ff0000) >> 16;                    int cg = (color & 0x0000ff00) >> 8;                    int cb = color & 0x000000ff;                    r += cr * gaussMatrix[j + radius];                    g += cg * gaussMatrix[j + radius];                    b += cb * gaussMatrix[j + radius];                    gaussSum += gaussMatrix[j + radius];                }            }            int index = y * width + x;            float avgR = r / gaussSum;            float avgG = g / gaussSum;            float avgB = b / gaussSum;            data[index] = (int)(avgR < 16 ? (avgR < 8 ? avgR : avgR | 0x00ff0000) : avgR | 0xff000000)                    | (int)(avgG < 16 ? (avgG < 8 ? avgG : avgG | 0x0000ff00) : avgG | 0x00ff0000)                    | (int)(avgB < 16 ? (avgB < 8 ? avgB : avgB | 0x000000ff) : avgB | 0x00000000);        }    }}

    4. 淡化颜色,生成素描图

    public static void colorDodge(int[] baseColor, int[] mixColor) {    for (int i = 0, length = baseColor.length; i < length; ++i) {        int bColor = baseColor[i];        int br = (bColor & 0x00ff0000) >> 16;        int bg = (bColor & 0x0000ff00) >> 8;        int bb = bColor & 0x000000ff;        int mColor = mixColor[i];        int mr = (mColor & 0x00ff0000) >> 16;        int mg = (mColor & 0x0000ff00) >> 8;        int mb = mColor & 0x000000ff;        int nr = colorDodgeFormular(br, mr);        int ng = colorDodgeFormular(bg, mg);        int nb = colorDodgeFormular(bb, mb);        baseColor[i] = nr < 16 ? (nr < 8 ? nr : nr | 0x00ff0000) : nr | 0xff000000                | ng < 16 ? (ng < 8 ? ng : ng | 0x0000ff00) : ng | 0x00ff0000                | nb < 16 ? (nb < 8 ? nb : nb | 0x000000ff) : nb | 0x00000000;    }}private static int colorDodgeFormular(int base, int mix) {    int result = base + (base * mix) / (255 - mix);    return result > 255 ? 255 : result;}

    5. 封装工具类,定义素描获取方法

    public static Bitmap testGaussBlur(Bitmap src, int r, int fai) {    int width = src.getWidth();    int height = src.getHeight();    int[] pixels = discolor(src);    int[] copixels = reverseColor(pixels);    gaussBlur(copixels, width, height, r, fai);    colorDodge(pixels, copixels);    Bitmap bitmap = Bitmap.createBitmap(pixels, width, height, Config.RGB_565);    return bitmap;}

    三、手指滑动调节图片透明度

    为了实现手指左右滑动调节图片透明度的效果,我们可以结合SeekBarGestureDetector来实现。

    1. 使用SeekBar实现基本效果

    public class SeekbarActivity extends AppCompatActivity implements View.OnTouchListener {    private static final String TAG = "SeekbarActivity";    private ImageView mImageView, mGestureImageView;    private SeekBar mSeekBar;    private TextView mTxt;    private int mProgress = 100;    private float mDistance = 1, maxDistance = 100;    private GestureDetector mGestureDetector;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_seekbar);        DisplayMetrics displayMetrics = new DisplayMetrics();        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);        int width = displayMetrics.widthPixels;        int height = displayMetrics.heightPixels;        mImageView = findViewById(R.id.alpha_img);        mGestureImageView = findViewById(R.id.ges_alpha_img);        mSeekBar = findViewById(R.id.alpha_seek);        mTxt = findViewById(R.id.alpha_txt);        mSeekBar.setMax(100);        mSeekBar.setProgress(100);        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {            @Override            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {                if (direction == 0) {                    if ((mProgress - progress) > 0 && (mProgress - progress) < 100) {                        mImageView.setImageBitmap(testGaussBlur(Utils.setAlpha(finalBitmap, mProgress - progress), 10, 10));                        mTxt.setText(String.valueOf(mProgress - progress) + "%");                    } else if ((mProgress - progress) < 0) {                        mImageView.setImageBitmap(testGaussBlur(finalBitmap, 10, 10));                        mTxt.setText(String.valueOf(1) + "%");                    }                } else {                    if ((mProgress + progress) < 100 && (mProgress + progress) > 0) {                        mImageView.setImageBitmap(testGaussBlur(Utils.setAlpha(finalBitmap, mProgress + progress), 10, 10));                        mTxt.setText(String.valueOf(mProgress + progress) + "%");                    } else if ((mProgress + progress) > 100) {                        mImageView.setImageBitmap(testGaussBlur(finalBitmap, 10, 10));                        mTxt.setText(String.valueOf(100) + "%");                    }                }            }            @Override            public void onStartTrackingTouch(SeekBar seekBar) {            }            @Override            public void onStopTrackingTouch(SeekBar seekBar) {                mProgress = seekBar.getProgress();            }        });        mImageView.setImageBitmap(Utils.setAlpha(finalBitmap, 100));        initGesture();    }    private void initGesture() {        mGestureDetector = new GestureDetector(new simpleGestureListener());        mGestureImageView.setOnTouchListener(this);        mGestureImageView.setFocusable(true);        mGestureImageView.setClickable(true);        mGestureImageView.setLongClickable(true);    }    @Override    public boolean onTouch(View v, MotionEvent event) {        return mGestureDetector.onTouchEvent(event);    }    private int direction = 0;    private class simpleGestureListener extends GestureDetector.SimpleOnGestureListener {        final int FLING_MIN_DISTANCE = 10;        final float MIN_DISTANCE = 0, MAX_DISTANCE = 100;        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {            if (Math.abs(distanceX) > FLING_MIN_DISTANCE) {                int progress = (int) (maxDistance - Math.abs(distanceX) / 15);                if (progress >= MIN_DISTANCE) {                    mGestureImageView.setImageBitmap(Utils.setAlpha(finalBitmap, progress));                    maxDistance = progress;                    mProgress = progress;                    mTxt.setVisibility(View.VISIBLE);                    mTxt.setText(String.valueOf(progress) + "%");                }            } else if (Math.abs(distanceX) < -FLING_MIN_DISTANCE) {                int progress = (int) (maxDistance - (-distanceX) / 5);                if (progress <= MAX_DISTANCE) {                    mGestureImageView.setImageBitmap(Utils.setAlpha(finalBitmap, progress));                    maxDistance = progress;                    mProgress = progress;                    mTxt.setVisibility(View.VISIBLE);                    mTxt.setText(String.valueOf(progress) + "%");                }            }            return true;        }        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {            mTxt.setVisibility(View.GONE);            mImageView.setImageBitmap(testGaussBlur(finalBitmap, 10, 10));            return true;        }    }}

    2. 使用GestureDetector实现手势控制

    public class GestureDetectorActivity extends AppCompatActivity implements View.OnTouchListener {    private static final String TAG = "GestureDetectorActivity";    private ImageView mImageView;    private GestureDetector mGestureDetector;    private Bitmap finalBitmap, bitmap;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_gesturedetector);        DisplayMetrics displayMetrics = new DisplayMetrics();        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);        int width = displayMetrics.widthPixels;        int height = displayMetrics.heightPixels;        mImageView = findViewById(R.id.gesture_img);        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.timg);        bitmap = Utils.compressBySampleSize(bitmap, width, height, false);        finalBitmap = testGaussBlur(bitmap, 10, 10);        mImageView.setImageBitmap(finalBitmap);        mGestureDetector = new GestureDetector(new simpleGestureListener());        mImageView.setOnTouchListener(this);        mImageView.setFocusable(true);        mImageView.setClickable(true);        mImageView.setLongClickable(true);    }    @Override    public boolean onTouch(View v, MotionEvent event) {        return mGestureDetector.onTouchEvent(event);    }    private int direction = 0;    private class simpleGestureListener extends GestureDetector.SimpleOnGestureListener {        final int FLING_MIN_DISTANCE = 10;        final float MIN_DISTANCE = 0, MAX_DISTANCE = 100;        public boolean onDown(MotionEvent e) {            return false;        }        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {            if (Math.abs(distanceX) > FLING_MIN_DISTANCE) {                int progress = (int) (MAX_DISTANCE - Math.abs(distanceX) / 15);                if (progress >= MIN_DISTANCE) {                    mImageView.setImageBitmap(Utils.setAlpha(finalBitmap, progress));                    MAX_DISTANCE = progress;                    direction = distanceX > 0 ? 0 : 1;                }            } else if (Math.abs(distanceX) < -FLING_MIN_DISTANCE) {                int progress = (int) (MAX_DISTANCE - (-distanceX) / 5);                if (progress <= MAX_DISTANCE) {                    mImageView.setImageBitmap(Utils.setAlpha(finalBitmap, progress));                    MAX_DISTANCE = progress;                    direction = distanceX < 0 ? 0 : 1;                }            }            return true;        }        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {            mImageView.setImageBitmap(finalBitmap);            return true;        }    }}

    通过以上代码,我们可以轻松实现将实拍照片转化为素描图的效果,并且通过手指滑动来调节图片的透明度,实现类似调节素描浓度的效果。

    转载地址:http://xqjg.baihongyu.com/

    你可能感兴趣的文章
    Objective-C实现n body simulationn体模拟算法(附完整源码)
    查看>>
    Objective-C实现naive string search字符串搜索算法(附完整源码)
    查看>>
    Objective-C实现natural sort自然排序算法(附完整源码)
    查看>>
    Objective-C实现nested brackets嵌套括号算法(附完整源码)
    查看>>
    Objective-C实现nevilles method多项式插值算法(附完整源码)
    查看>>
    Objective-C实现newton raphson牛顿-拉夫森算法(附完整源码)
    查看>>
    Objective-C实现newtons second law of motion牛顿第二运动定律算法(附完整源码)
    查看>>
    Objective-C实现newton_forward_interpolation牛顿前插算法(附完整源码)
    查看>>
    Objective-C实现newton_raphson牛顿拉夫森算法(附完整源码)
    查看>>
    Objective-C实现ngram语言模型算法(附完整源码)
    查看>>
    Objective-C实现NLP中文分词(附完整源码)
    查看>>
    Objective-C实现NLP中文分词(附完整源码)
    查看>>
    Objective-C实现NMS非极大值抑制(附完整源码)
    查看>>
    Objective-C实现NMS非极大值抑制(附完整源码)
    查看>>