一種通過多線程快速插入ARGB到YUV圖片的原創方法 C語言實現

利用之前寫的顏色混合原理,在實現了ARGB轉成YUV後,通過alpha值按比例混合原有的YUV值即可:

顏色混合

具體實現代碼:

fastYuvInsertBmp.c

過程中我使用了一個循環做了個假的三色bmp,你可以用真的bmp代替進行測試。精華在那個雙重循環裏面,留個坑以後解釋位置偏移原理:

#include "stdio.h"
#include "stdlib.h"
#include "multiThread.c"
#include <sys/time.h>

struct params
{
    char* yuvData; 
    int* bmpData; 
    int yuvW; 
    int yuvH; 
    int bmpH; 
    int bmpW; 
    int top; 
    int left; 
    float startRation; 
    float endRation;
};
typedef struct params Params;

struct timeval stamp;


void yuvInsert(char* yuvData, int* bmpData, int yuvW, int yuvH, int bmpH, int bmpW, int top, int left);
void* yuvInsertProcess(void *params);

int main(int argc, char* argv[]) {
    FILE *f = fopen("/media/chenjiezhu/work2/其他/yuv_argb_mix/test/getSourceYuv/1592896365132_4672x2128_NV21.NV21", "rb");
    FILE *f2 = fopen("/media/chenjiezhu/work2/其他/yuv_argb_mix/test/getSourceYuv/1592896365132_4672x2128_NV21_change.NV21", "wb+");
    int yuvW = 4672;
    int yuvH = 2128;
    int bmpW = 500;
    int bmpH = 500;

    int *bmpData = (int*) malloc(sizeof(int) * bmpW * bmpH);
    char *yuvData = (char*) malloc(sizeof(char) * yuvW * yuvH * 1.5f);
    printf("the f is %d\n", f);
    int i;
    for(i = 0; i < bmpW * bmpH; i++) {
            if(i > bmpW * (int)(bmpH * 2 / 3.0f)) {
                bmpData[i] = ((100 << 24) | (0xFF << 16));
                continue;
            } 
            if(i > bmpW * (int)(bmpH * 1 / 3.0f)) {
                bmpData[i] = ((100 << 24) | (0xF0 << 8));
                continue;
            } 
            if(i > 0) {
                bmpData[i] = ((100 << 24) | (0xFF));
                continue;
            }
    }
    fread(yuvData, 1, yuvW * yuvH * 1.5f, f);
    fclose(f);
    yuvInsert(yuvData, bmpData, yuvW, yuvH, bmpW, bmpH, 500, 500);
    mySleep(1000 * 1000);
    printf("運行結束\n");
    fwrite(yuvData, 1, yuvW * yuvH * 1.5f, f2);
    fclose(f2);
    return 0;  
}


/*top,left用於位圖寫到yuv圖像的哪個座標作爲左上角用的 */
void yuvInsert (char* yuvData, int* bmpData, int yuvW, int yuvH, int bmpW, int bmpH, int top, int left) {
    //yuvInsertProcess(yuvData, bmpData, yuvW, yuvH, bmpH, bmpW, top, left, 0.0f, 1.0f);
    int threadCount = 4;
    threadFinishedCount = 0;
    struct timeval stamp;
    ThreadFun funArr[threadCount];
    int i;
    for(i = 0; i < threadCount; i++) {
        Params *params = (Params*) malloc(sizeof(Params));
        params->bmpData = bmpData;
        params->bmpH = bmpH;
        params->bmpW = bmpW;
        params->startRation = i / (threadCount * 1.0f);
        params->endRation =   (i + 1) / (threadCount * 1.0f);
        params->left = left;
        params->top = top;
        params->yuvData = yuvData;
        params->yuvH = yuvH;
        params->yuvW = yuvW;
	    funArr[i].fun = yuvInsertProcess;
	    funArr[i].params = params;
    }
    gettimeofday(&stamp, NULL);
    long startTime = stamp.tv_sec * 1000000 + stamp.tv_usec;
    startThread(threadCount, funArr);
    // for(i = 0; i < threadCount; i++) {
    //     funArr[i].fun(funArr[i].params);
    // }

    while (threadFinishedCount < threadCount) {
        mySleep(1);
    }
    gettimeofday(&stamp, NULL);
    long endTime = stamp.tv_sec * 1000000 + stamp.tv_usec;
    printf("finish time: %ld ms\n", (endTime - startTime) / 1000);
    LOGI("finish time: %ld ms\n", (endTime - startTime) / 1000);
}

void* yuvInsertProcess (void *params) {
    Params *p = (Params*) params;
    int *bmpData = p->bmpData;
    char *yuvData = p->yuvData; 
    int yuvW = p->yuvW;
    int yuvH = p->yuvH;
    int bmpW = p->bmpW;
    int bmpH = p->bmpH;
    int area = yuvW * yuvH;
    int bmpArea = bmpW * bmpH;
    float startRation = p->startRation;
    float endRation = p->endRation;
    int left = p->left;
    int top = p->top;
       /*i : 每行起始位置
      j : 位圖遊標,定位讀位圖的哪個位置的像素
      count : 瞭解已經操作bmp和yuv的第幾行
      k : 讀bmp並轉換到yuv的內循環遊標
      l : 內循環(行遍歷循環)遊標*/
    int i, j, k, l, count;
    LOGI("startTrans, startRation:%f, endRation:%f\n", startRation, endRation);
    //每次讀一行yuv和一行bmp
    int yuvStartPos = (top + (int)(bmpH * startRation)) * yuvW + left; //800000 + 500 + 960000
    // int yuvStartPos = 1760500;
    int bmpStartPos = 0 + bmpW * (int)(bmpH * startRation);  //0 + 125000
    // int bmpStartPos = 125000;
    for(i = yuvStartPos, j = bmpStartPos, count = 1 + startRation * bmpH; 
        i < yuvStartPos + yuvW * /*(int)*/ (yuvH * (endRation - startRation)) && j < bmpStartPos + bmpW * /*(int)*/ (bmpH * (endRation - startRation)); 
        i += yuvW, j += bmpW, count++) 
    { 
            //printf("i = %d, j = %d, count = %d\n", i, j, count);
        for(k = j, l = 0; k < bmpW * count; k++, l++) {  //count 行號
            if (k > bmpW * bmpH) {
                break;
            }
            /* argb按透明度作爲比例轉yuv,大小端問題把R和B通道倒置*/
            float alpha = (bmpData[k] >> 24 & 0xFF);
            float ratio = alpha / 255.0f;
            float rationReverse = (255 - alpha) / 255.0f;

            unsigned int R = (bmpData[k] & 0xFF);
            unsigned int G = (bmpData[k] >> 8  & 0xFF);
            unsigned int B = (bmpData[k] >> 16 & 0xFF);
            unsigned char argbToY = (unsigned char) ((77 * R + 150 * G + 29 * B) >> 8);
            // unsigned char argbToY = (unsigned char) ((66 * R + 129 * G + 25 * B) >> 8 + 16);
            argbToY < 0 ? 0 : ((argbToY > 255) ? 255 : argbToY);
            /* yuv按透明度作爲比例和剛剛轉好的新yuv值混合*/
            unsigned char oldY = yuvData[i + l]; 
            yuvData[i + l] = argbToY * ratio + oldY * rationReverse; //新的Y值

            if ((count - 1) % 2 == 0 && l % 2 == 0) { //隔行隔列寫入uv數據
                int pos = yuvW * yuvH + ((count - 1) / 2 + top / 2) * yuvW + left + l;
                unsigned char argbToU = (unsigned char) (((-44 * R - 87 * G + 131 * B) >> 8) + 128);
                unsigned char argbToV = (unsigned char) (((131 * R - 110 * G - 21 * B) >> 8) + 128);      
                unsigned char oldV = yuvData[pos];
                unsigned char oldU = yuvData[pos + 1];
            
                
                int newV = argbToV * ratio + oldV * rationReverse;//新的V值
                int newU = argbToU * ratio + oldU * rationReverse;//新的U值
                // yuvData[pos] = (newV < 0) ? 0 : ((newV > 255) ? 255 : newV);
                // yuvData[pos + 1] = (newU < 0) ? 0 : ((newU > 255) ? 255 : newU);
                yuvData[pos] = newV;
                yuvData[pos + 1] = newU;
                //printf("old value: %d, new value argbToY :%d \t", old, argbToY);
            }
        }
    }
    free(params);
    threadFinishedCount ++;
}

多線程工具multiThread.c:

#include <stdio.h>  
#include <stdlib.h>  
#include <limits.h>  
#include <time.h>  
  
#ifdef _WIN32  
	#include <windows.h>  
	#include <direct.h>  
	#include <io.h>  
	#include <process.h>  
#else  
	#include <unistd.h>  
	#include <getopt.h>  
	#include <sys/types.h>  
	#include <pthread.h>  
#endif  
 
#define new(Class) (Class*)malloc(sizeof(Class))  
 
typedef struct ThreadFun ThreadFun;
struct ThreadFun{
	void* params;  //線程函數的參數 
	void* (*fun)(void *params);   //線程函數指針 
};
 
void mySleep(long ms){
#ifdef _WIN32  
	Sleep(ms);
#else
	usleep(ms);
#endif  	
}
 
void startThread(int threadNum, ThreadFun funArray[]){
 
#ifdef _WIN32  
    HANDLE handle[threadNum];
	int i = 0;
	for(i = 0; i < threadNum; i++){
	   handle[i] = (HANDLE) _beginthreadex(NULL, 0, funArray[i].fun, funArray[i].params, 0, NULL); // create thread ok  
	}
	//DWORD dwThread;  
    //HANDLE handle = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)fun,(LPVOID)NULL,0,&dwThread); // create thread ok  
    printf("Win32\n");
	//WaitForMultipleObjects(threadNum, handle, TRUE, INFINITE); //等待這幾個線程結束 
    for(i = 0; i < threadNum; i++){
		CloseHandle(handle[i]);    //關閉句柄 
	}
#else  
    
    pthread_mutex_t mutex;
	pthread_cond_t cond;
    pthread_t pt[threadNum];
    int i = 0;
    
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
    for(i = 0; i < threadNum; i++){
    	pthread_create(&pt[i], NULL, funArray[i].fun, funArray[i].params); 
	}
	// pthread_exit(0);  //等待全部線程結束函數才結束 
    
#endif  
}
 
/*void shit(void *p){
	while(1) {
		printf("%s\n", (char*) p);
		mySleep(1);
	}
}
 
void* shit2(void *p){
	while(1) {
		printf("%s\n", (char*) p);
		mySleep(1);
	}
}
 
	
int main(){
	ThreadFun funArr[2];
	funArr[0].fun = shit;
	funArr[0].params = "asdasd";
	funArr[1].fun = shit2;
	funArr[1].params = "111232323";
	startThread(2, funArr);
	while(1){
		printf("shit\n");
		mySleep(1);
	}
	return 0;
}*/

實現效果:

 

可以看到,yuv圖片和我的bmp圖片按照bmp的透明圖混合在一起了。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章