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

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

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实现EEMD算法(附完整源码)
    查看>>
    Objective-C实现elgamal 密钥生成器算法(附完整源码)
    查看>>
    Objective-C实现EM算法(附完整源码)
    查看>>
    Objective-C实现EM算法(附完整源码)
    查看>>
    Objective-C实现entropy熵算法(附完整源码)
    查看>>
    Objective-C实现euclidean distance欧式距离算法(附完整源码)
    查看>>
    Objective-C实现Euclidean GCD欧几里得最大公约数算法(附完整源码)
    查看>>
    Objective-C实现euclideanDistance欧氏距离算法(附完整源码)
    查看>>
    Objective-C实现euler method欧拉法算法(附完整源码)
    查看>>
    Objective-C实现euler modified变形欧拉法算法(附完整源码)
    查看>>
    Objective-C实现eulerianPath欧拉路径算法(附完整源码)
    查看>>
    Objective-C实现Eulers TotientFunction欧拉函数算法(附完整源码)
    查看>>
    Objective-C实现eulers totient欧拉方程算法(附完整源码)
    查看>>
    Objective-C实现EulersTotient欧拉方程算法(附完整源码)
    查看>>
    Objective-C实现eval函数功能(附完整源码)
    查看>>
    Objective-C实现even_tree偶数树算法(附完整源码)
    查看>>
    Objective-C实现Exceeding words超词(差距是ascii码的距离) 算法(附完整源码)
    查看>>
    Objective-C实现exchange sort交换排序算法(附完整源码)
    查看>>
    Objective-C实现ExponentialSearch指数搜索算法(附完整源码)
    查看>>
    Objective-C实现extended euclidean algorithm扩展欧几里得算法(附完整源码)
    查看>>