directory
- Basic knowledge of colors and filters
- Practice: Color adjustment through ColorFilter
- Practice: Picture filter (black and white, warm and cold colors)
- Problems encountered
- data
- harvest
First, basic knowledge of color and filter
How do we see the different colors?
Image source: [The development of color vision auxiliary function of the player helps improve the video viewing experience of users with color vision impairment]
Different wavelengths of light have different colors. In our visible light range, blue light has short wavelengths and long wavelengths appear red. We humans have three different types of cones, which are sensitive to different kinds of light, and are responsible for 4 to 6 percent of the world’s achromatic or color-blind people.
Image source: [The development of color vision auxiliary function of the player helps improve the video viewing experience of users with color vision impairment]
The filters we learn in this article can be used as visual correction for people with visual impairment. At the same time, for people with normal vision, the color can also be changed by filter, which is suitable for different scenes. For example, the mourning day mode, beauty and filter functions can be applied in short videos or live broadcasts more widely, so as to add some fun.
Now that we know the background, let’s learn about color from the three elements of color. Color three elements: hue (hue), saturation, brightness
Hue is the most accurate standard to distinguish all kinds of different colors. Any color other than black, white and gray has the attribute of hue, and hue is composed of primary color, intermediate color and complex color. Hue, the qualitative aspect that colors can present. Depending on the color arrangement of the color ring, adjacent colors are mixed and the saturation is basically unchanged (such as orange obtained by mixing red and yellow). Contrast hue is mixed, reduce saturation most easily, down to become gloomy colour brightness decides object illume degree not only, and the reflection coefficient that decides object surface. If the light we see comes from a light source, the brightness depends on the intensity of the light source. If we see light reflected from a surface, then the brightness depends on the intensity of the illuminant and the reflection coefficient of the surface. From the Encyclopedia of Three Elements of Color
Image from: Color Ring Encyclopedia
In android, there is ColorMatrix ColorMatrix tool class, which helps us encapsulate and realize the adjustment of the three elements of color and the realization of multiplication of different matrices.
tonal
public void setRotate(int axis, float degrees) { reset(); double radians = degrees * Math.PI / 180d; float cosine = (float) Math.cos(radians); float sine = (float) Math.sin(radians); switch (axis) { // Rotation around the red color case 0: mArray[6] = mArray[12] = cosine; mArray[7] = sine; mArray[11] = -sine; break; // Rotation around the green color case 1: mArray[0] = mArray[12] = cosine; mArray[2] = -sine; mArray[10] = sine; break; // Rotation around the blue color case 2: mArray[0] = mArray[6] = cosine; mArray[1] = sine; mArray[5] = -sine; break; default: throw new RuntimeException(); }}Copy the code
You can see that hue adjustment takes two parameters: axis which color is rotated around, and degress which Angle is rotated. The range is -180 degrees, 180 degrees. For example, the matrix below is the result when axis is red.
Saturation The saturation of bright colors
public void setSaturation(float sat) {
reset();
float[] m = mArray;
final float invSat = 1 - sat;
final float R = 0.213f * invSat;
final float G = 0.715f * invSat;
final float B = 0.072f * invSat;
m[0] = R + sat; m[1] = G; m[2] = B;
m[5] = R; m[6] = G + sat; m[7] = B;
m[10] = R; m[11] = G; m[12] = B + sat;
}
Copy the code
Sat represents the degree of saturation. Range is [0, 1] For example, when SAT is 0, RGB corresponding values are 0.213f, 0.715f, 0.072f, this is to achieve black and white mode
brightness
public void setScale(float rScale, float gScale, float bScale,
float aScale) {
final float[] a = mArray;
for (int i = 19; i > 0; --i) {
a[i] = 0;
}
a[0] = rScale;
a[6] = gScale;
a[12] = bScale;
a[18] = aScale;
}
Copy the code
The four parameters respectively represent the range of rgba’s four channels, and the range is between [0,1].
From the above introduction, we learned that you can modify the three elements of the color to achieve the function of the filter. Let’s start with our practice.
Two, practice: ColorFilter color change View
We first set fixed values through the color matrix to modify the color of ordinary pictures, to achieve black and white, warm and cold color changes, and then dynamically adjust the tone, saturation, and brightness to achieve the color adjustment of the picture. Let’s begin this section of our journey.
First, we can directly set the View Bitamp ColorFilter, to achieve the color change
/ / Gray, black and white mode = R + G + B * 0.59 * 0.11 * 0.3 float [] mBWMatrix = {0.3 f to 0.59 f, f 0.11, 0, 0, 0.3 f to 0.59 f to 0.11 f, 0, 0, 0.3 f, f 0.59, 0.11 f, 0, 0, 0,0,0,1,0}; / / warm treatment can increase the value of red and green channel float [] mWarmMatrix = {,0,1,0,0,0,0,0,0 2, 0,2,0,0,0, 0, 0,0,0,1,0}; Float [] mCoolMatrix = {1,0,0,0,0, 0,2,0, 0,0,0,1,0}; public void setImageMatrix(float[] mColorMatrix) { Bitmap bmp = Bitmap.createBitmap(mBitmap.getWidth(),mBitmap.getHeight(),Bitmap.Config.ARGB_8888); ColorMatrix colorMatrix = new ColorMatrix(); colorMatrix.set(mColorMatrix); Canvas canvas = new Canvas(bmp); Paint paint = new Paint(); paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); Canvas. DrawBitmap (mBitmap, 0, 0, paint); ivImage.setImageBitmap(bmp); }Copy the code
We can also directly modify the color saturation to achieve black and white
public static Bitmap handleImageEffect(Bitmap oriBmp, float hue, float saturation, float lum) {
Bitmap bmp = Bitmap.createBitmap(oriBmp.getWidth(), oriBmp.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
Paint paint = new Paint();
ColorMatrix saturationMatrix = new ColorMatrix();
saturationMatrix.setSaturation(saturation);
paint.setColorFilter(new ColorMatrixColorFilter(saturationMatrix));
canvas.drawBitmap(oriBmp, 0, 0, paint);
return bmp;
}
Copy the code
The effect is as follows:
ColorMatrix also provides matrix multiplication, so you can change the hue, saturation, and brightness of the image at the same time.
private float mSaturaion =1; private float mLum=1; private float mHue=0; @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { int id = seekBar.getId(); Switch (id){case R.id.sb_HUE: mHue = (progress-midValue) * 1.0f/midValue * 180; break; Case R.I.S.sb_saturation: mSaturaion = Progress *1.0f/midValue; break; Case r.i.sub_lum: mLum = progress*1.0f/midValue; break; } Log.d(TAG, "onProgressChanged: midValue="+midValue+" mhue="+mHue+" msaturation="+mSaturaion+" mlum="+mLum+" progress="+progress); ivImage.setImageBitmap(handleImageEffect(mBitmap,mHue,mSaturaion,mLum)); } /** * Hue, saturation and brightness are multiplied by PostConcat of ColorMatrix, Adjust the hue range from 180 ° to 180 ° * @param saturation * @param lum * @return */ public static Bitmap handleImageEffect(Bitmap oriBmp, float hue, float saturation, float lum) { Bitmap bmp = Bitmap.createBitmap(oriBmp.getWidth(), oriBmp.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bmp); Paint paint = new Paint(); Log. I (TAG, "handleImageEffect: hue ="+hue); ColorMatrix hueMatrix = new ColorMatrix(); hueMatrix.setRotate(0, hue); // Rotate the hue Angle around red huematrix.setrotate (1, hue); // Rotate the hue Angle around green huematrix.setrotate (2, hue); Log. I (TAG, "handleImageEffect: saturation="+saturation); ColorMatrix saturationMatrix = new ColorMatrix(); saturationMatrix.setSaturation(saturation); Log. I (TAG, "handleImageEffect: brightness lum="+lum); ColorMatrix lumMatrix = new ColorMatrix(); lumMatrix.setScale(lum, lum, lum, 1); ColorMatrix imageMatrix = new ColorMatrix(); imageMatrix.postConcat(hueMatrix); imageMatrix.postConcat(saturationMatrix); imageMatrix.postConcat(lumMatrix); paint.setColorFilter(new ColorMatrixColorFilter(imageMatrix)); canvas.drawBitmap(oriBmp, 0, 0, paint); return bmp; }Copy the code
The effect is as follows:
Three, practice: OpenGL ES image filter
In this section, we realized the color change through OpenGL ES. The specific process is as follows
- Load shaders and draw frames in the Render of GlSurfaceView
- Pass different filter types to GLSL through external Settings, and carry out different color changes according to different types in GLSL.
The vertex shader is the same as in the previous OpenGL ES texture adding article. For the slice shader, we will add two uniform types, representing the filter type and the filter’s color vector.
//texture_vertex_shader.glsl uniform mat4 u_Matrix; attribute vec4 a_Position; attribute vec3 a_Color; attribute vec2 a_TextureCoordinates; varying vec2 v_TextureCoordinates; varying vec3 v_Color; void main() { v_TextureCoordinates = a_TextureCoordinates; v_Color = a_Color; gl_Position = a_Position; } //image_filter_texture_fragment_shader.glsl precision mediump float; uniform sampler2D u_TextureUnit; uniform int u_TypeIndex; varying vec2 v_TextureCoordinates; varying vec3 v_Color; void main() { vec4 color = texture2D(u_TextureUnit, v_TextureCoordinates); if (u_TypeIndex == 0){ gl_FragColor = color; } else if (u_TypeIndex == 1){ float c = color.r * v_Color.r + color.g * v_Color.g + color.b * v_Color.b; Gl_FragColor = vec4(c, c, c, 1.0f); } else {vec4 newColor = color + vec4(v_Color, 0.0f); gl_FragColor = newColor; }}Copy the code
How to load shaders and set values for properties in the shader in Render
public class ImageFilterRender implements GLSurfaceView.Renderer { private Context context; private int textureId; private TextureShaderProgram textureProgram; private BgTextureObject textureObject; public ImageFilterRender(Context context) { this.context = context; } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { GLES10.glClearColor(0f, 0f, 0f, 0f); textureObject = new BgTextureObject(ImageBgData.VERTEX_DATA); String fragmentCode = ShaderHelper.loadAsset(MyApplication.getContext().getResources(), "image_filter_texture_fragment_shader.glsl"); String vertexCode = ShaderHelper.loadAsset(MyApplication.getContext().getResources(), "texture_vertex_shader.glsl"); textureProgram = new TextureShaderProgram(context, vertexCode, fragmentCode); textureId = TextureHelper.loadTexture(context, R.drawable.bg); } private void refreshTexureProgram(String fragmentCode) { } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { GLES20.glViewport(0, 0, width, height); } @Override public void onDrawFrame(GL10 gl) { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); textureProgram.useProgram(); // GLES20.glUniform1i(textureProgram.getFilterIndexUniformLocation(), mIndex); Switch (mIndex){case 0: // break; Case 1: / / black and white filter GLES20 glVertexAttrib3fv (textureProgram. GetProgram (), ImageBgData. GRAY_FILTER_COLOR_DATA, 0). break; Case 2: / / warm color filter GLES20 glVertexAttrib3fv (textureProgram. GetProgram (), ImageBgData. WARM_FILTER_COLOR_DATA, 0). break; Case 3: / / cool color filter GLES20 glVertexAttrib3fv (textureProgram. GetProgram (), ImageBgData. COOL_FILTER_COLOR_DATA, 0). break; } textureProgram.setUniforms(textureId); textureObject.bindData(textureProgram); textureObject.draw(); } private int mIndex; public void setFilter(int index) { mIndex = index; }}Copy the code
Results the following
The source code has been uploaded to Github
4. Problems encountered
Fault 1. When reloading a Shader, an error message is displayed: ShaderHelper: loadProgram: glCreateProgram Error errorCode=0
The purpose is to switch the effect of original picture, black and white, warm color and cold color by clicking the button. After reloading shaderCode and program, I reported the above error. Later I thought that it would be more reasonable to tell GLSL by setting different types of filters, and then process it inside GLSL, so that the effect can be seen when onDrawFrame is refreshed, instead of recreating the program.
Problem 2: ‘u_TypeIndex’ : Syntax error: Syntax error_
Use vec1 or IVec1, at least two vectors, GLSL itself supports int, float, I’m just passing a tpYE to the slice shader to use different algorithm logic within the slice shader for different filters.
How to convert a recorded MP4 into an appropriate GIF image for uploading
Ffmpeg-i colorFilter2.mp4 -r 16 -s 320x480-filter :v "setpts=0.5*PTS" -b 240K colorFilter2.gif -r 16: Frame rate 16fps -s 320x480: width and height -filter:v "setpts=0.5*PTS" : double speedCopy the code
Five, the data
Android Filter effect realization and Principle Analysis [Android OpenGL ES(4)- add a filter to the graph] [Android learning note 22: ColorMatrix]
Six, harvest
- Learn the basics of colors and filters
- Modify color transitions (black and white, warm and cool) with ColorFilter
- Use openGl ES to modify the colors to achieve the filter effect (black and white, warm and cool colors)
- The cognitive learning of GLSL was strengthened
Thank you for reading
In the next article, we will continue to learn practical filters, real-time filters of Camera. Welcome to pay attention to the public account “audio and video development journey”, learn and grow together.
The original link