diff --git a/README.md b/README.md index a6db215..79c4f39 100644 --- a/README.md +++ b/README.md @@ -32,3 +32,13 @@ and vcsm_free(state.lens_shading); ``` when finished. + +ls_table.txt is a comma separated file for easy visualization with Gnuplot. For a colored plot of all samples: +``` +set palette defined (0 "red", 1 "yellow", 2 "magenta", 3 "blue") +splot "ls_table.txt" using 1:2:3:4 w p ps 0.75 pt 7 lc palette z notitle +``` +Single sample plot ($4==0 => red): +``` +splot "ls_table.txt" using 1:2:($4==0?$3:1/0) +``` diff --git a/lens_shading_analyse.c b/lens_shading_analyse.c old mode 100755 new mode 100644 index 1710263..16c10c2 --- a/lens_shading_analyse.c +++ b/lens_shading_analyse.c @@ -102,6 +102,7 @@ struct brcm_raw_header { //Values taken from https://github.com/raspberrypi/userland/blob/master/interface/vctypes/vc_image_types.h #define BRCM_FORMAT_BAYER 33 #define BRCM_BAYER_RAW10 3 +#define BRCM_BAYER_RAW12 4 enum bayer_order_t { RGGB, @@ -117,41 +118,131 @@ const int channel_ordering[4][4] = { { 1, 0, 3, 2 } }; +uint8_t* sensor_model_check(int sensor_model, void* buffer, size_t size) +{ + uint8_t* in_buf = 0; + + switch(sensor_model) { + case 1: + in_buf = ((uint8_t*)buffer) + size - 6404096; + break; + case 2: + in_buf = ((uint8_t*)buffer) + size - 10270208; + break; + case 3: + in_buf = ((uint8_t*)buffer) + size - 18711040; + break; + default: + return 0; + break; + } + + if (memcmp(in_buf, "BRCM", 4) == 0) + { + return in_buf; + } + else + { + return 0; + } +} + uint16_t black_level_correct(uint16_t raw_pixel, unsigned int black_level, unsigned int max_value) { return ((raw_pixel - black_level) * max_value) / (max_value - black_level); } +void print_help(void) +{ + printf("\n"); + printf("\n"); + printf("\"lens_shading_analyse\" Lens shading analysis tool\n"); + printf("\n"); + printf("Analyzes the lens shading based on a raw image\n"); + printf("\n"); + printf("usage: lens_shading_analyse -i [options]\n"); + printf("\n"); + printf("Parameters\n"); + printf("\n"); + printf("-i : Raw image file (mandatory)\n"); + printf("-b : Black level\n"); + printf("-s : Size of the analysis cell. Minimum 2, maximum 32, default 4\n"); + printf("-o : Output format. Formats can be output together, for example 3 = 1 + 2\n"); + printf(" 1 : Header file (default on)\n"); + printf(" 2 : Binary file\n"); + printf(" 4 : Text file\n"); + printf(" 8 : Channel data\n"); + printf("\n"); +} + int main(int argc, char *argv[]) { int in = 0; - FILE *out, *header; + FILE *out, *header, *table, *bin; int i, x, y; uint16_t *out_buf[NUM_CHANNELS]; + uint16_t max_val; void *mmap_buf; uint8_t *in_buf; struct stat sb; + int bits_per_sample; int bayer_order; struct brcm_raw_header *hdr; int width, height, stride; + uint32_t grid_width, grid_height, block_px_max; int single_channel_width, single_channel_height; - unsigned int black_level = 16; + unsigned int black_level = 0; + uint32_t *block_sum; + uint8_t block_size = 4; + uint8_t out_frmt = 1; if (argc < 2) { - printf("%s [black level]\n", argv[0]); + print_help(); return -1; } - in = open(argv[1], O_RDONLY); - if (in < 0) - { - printf("Failed to open %s\n", argv[1]); - return -1; - } - if (argc >= 3) + int nArg; + while ((nArg = getopt(argc, argv, "b:i:o:s:")) != -1) { - black_level = strtoul(argv[2], NULL, 10); + switch (nArg) { + case 'b': + black_level = strtoul(optarg, NULL, 10); + break; + case 'i': + in = open(optarg, O_RDONLY); + if (in < 0) + { + printf("Failed to open %s\n", argv[1]); + return -1; + } + break; + case 'o': + out_frmt = strtoul(optarg, NULL, 10); + if (!out_frmt & 0x0F) + { + printf("Invalid output format\n"); + return -1; + } + break; + case 's': + block_size = strtoul(optarg, NULL, 10); + if (block_size<=0 || block_size>32) + { + printf("Analysis cell out of range\n"); + return -1; + } + else if (block_size%2 == 1) + { + block_size++; + } + break; + default: + case 'h': + print_help(); + return -1; + break; + } } fstat(in, &sb); @@ -166,20 +257,16 @@ int main(int argc, char *argv[]) if (!memcmp(mmap_buf, "\xff\xd8", 2)) { - //JPEG+RAW - find the raw header - //Try the appropriate offsets for the full res modes - //of OV5647 and IMX219. Any other modes will need to be - //stripped down to the bare raw (inc header) before processing - in_buf = ((uint8_t*)mmap_buf) + sb.st_size - 6404096; - if (memcmp(in_buf, "BRCM", 4)) + int sensor_model = 1; + do { - //Failed on OV5647, try IMX219 - in_buf = ((uint8_t*)mmap_buf) + sb.st_size - 10270208; - if (memcmp(in_buf, "BRCM", 4)) - { - //Failed totally - reset to the start of the buffer - in_buf = (uint8_t*)mmap_buf; - } + in_buf = sensor_model_check(sensor_model, mmap_buf, sb.st_size); + } + while(in_buf == 0 && sensor_model++ <= 3); + + if (in_buf == 0) + { + in_buf = (uint8_t*)mmap_buf; } } else @@ -193,23 +280,70 @@ int main(int argc, char *argv[]) goto unmap; } + char model[7]; + memcpy(model, &in_buf[16], 6); + model[6] = '\0'; + if (strncmp(model, "imx219", 6) == 0) + { + printf("Sensor type: %s\n", model); + if (black_level == 0) + { + black_level = 64; + } + } + else if (strncmp(model, "ov5647", 6) == 0) + { + printf("Sensor type: %s\n", model); + if (black_level == 0) + { + black_level = 16; + } + } + else if (strncmp(model, "testc", 6) == 0 || + strncmp(model, "imx477", 6) == 0) + { + printf("Sensor type: %s\n", model); + if (black_level == 0) + { + black_level = 257; + } + } + else if (black_level == 0) + { + black_level = 16; // Default value + } + printf("Black level: %d\n", black_level); + hdr = (struct brcm_raw_header*) (in_buf+0xB0); printf("Header decoding: mode %s, width %u, height %u, padding %u %u\n", hdr->name, hdr->width, hdr->height, hdr->padding_right, hdr->padding_down); printf("transform %u, image format %u, bayer order %u, bayer format %u\n", hdr->transform, hdr->format, hdr->bayer_order, hdr->bayer_format); - if (hdr->format != BRCM_FORMAT_BAYER || hdr->bayer_format != BRCM_BAYER_RAW10) + if (hdr->format != BRCM_FORMAT_BAYER || + (hdr->bayer_format != BRCM_BAYER_RAW10 && hdr->bayer_format != BRCM_BAYER_RAW12)) { - printf("Raw file is not Bayer raw10\n"); + printf("Raw file is not Bayer raw10 or raw12\n"); goto unmap; } bayer_order = hdr->bayer_order; + bits_per_sample = hdr->bayer_format * 2 + 4; + max_val = ( 1 << bits_per_sample ) - 1; width = hdr->width; height = hdr->height; single_channel_width = width/2; single_channel_height = height/2; - //Stride computed via same formula as the firmware uses. - stride = (((((width + hdr->padding_right)*5)+3)>>2) + 31)&(~31); + grid_width = (single_channel_width + 31) / 32; + grid_height = (single_channel_height + 31) / 32; + block_px_max = block_size*block_size; + block_sum = (uint32_t *)malloc(sizeof(uint32_t) * grid_width * grid_height); + printf("Grid size: %d x %d\n", grid_width, grid_height); + + if (bits_per_sample == 10) { + //Stride computed via same formula as the firmware uses. + stride = (((((width + hdr->padding_right)*5)+3)>>2) + 31)&(~31); + } else { + stride = (((((width + hdr->padding_right)*6)+3)>>2) + 31)&(~31); + } for (i=0; i>1)*single_channel_width); uint16_t *chan_b_line = out_buf[chan_b] + ((y>>1)*single_channel_width); - for (x=0; x>6), black_level, (1<<10)-1); - chan_a_line++; - lsbs<<=2; - line++; - *(chan_b_line) = black_level_correct(((*line)<<2) + (lsbs>>6), black_level, (1<<10)-1); - chan_b_line++; - lsbs<<=2; - line++; - *(chan_a_line) = black_level_correct(((*line)<<2) + (lsbs>>6), black_level, (1<<10)-1); - chan_a_line++; - lsbs<<=2; - line++; - *(chan_b_line) = black_level_correct(((*line)<<2) + (lsbs>>6), black_level, (1<<10)-1); - chan_b_line++; - lsbs<<=2; - line++; - line++; //skip the LSBs + if (bits_per_sample == 10) { + for (x=0; x>6), black_level, max_val); + chan_a_line++; + lsbs<<=2; + line++; + *(chan_b_line) = black_level_correct(((*line)<<2) + (lsbs>>6), black_level, max_val); + chan_b_line++; + lsbs<<=2; + line++; + *(chan_a_line) = black_level_correct(((*line)<<2) + (lsbs>>6), black_level, max_val); + chan_a_line++; + lsbs<<=2; + line++; + *(chan_b_line) = black_level_correct(((*line)<<2) + (lsbs>>6), black_level, max_val); + chan_b_line++; + lsbs<<=2; + line++; + line++; //skip the LSBs + } + } else { + for (x=0; x>4), black_level, max_val); + chan_a_line++; + line++; + *(chan_b_line) = black_level_correct(((*line)<<4) + (line[ 1 ]&0x0F), black_level, max_val); + chan_b_line++; + line+= 2; + *(chan_a_line) = black_level_correct(((*line)<<4) + (line[ 2 ]>>4), black_level, max_val); + chan_a_line++; + line++; + *(chan_b_line) = black_level_correct(((*line)<<4) + (line[ 1 ]&&0x0F), black_level, max_val); + chan_b_line++; + line+= 2; + } } } - printf("Save data. Bayer order is %d\n", bayer_order); - - header = fopen("ls_table.h", "wb"); - fprintf(header, "uint8_t ls_grid[] = {\n"); + if (out_frmt&0x01) + { + header = fopen("ls_table.h", "wb"); + } + if (out_frmt&0x02) + { + bin = fopen("ls.bin", "wb"); + } + if (out_frmt&0x04) + { + table = fopen("ls_table.txt", "wb"); + } + if (out_frmt&0x01) + { + fprintf(header, "uint8_t ls_grid[] = {\n"); + } + if (out_frmt&0x02) + { + uint32_t transform = hdr->transform; + fwrite(&transform, sizeof(uint32_t), 1, bin); + fwrite(&grid_width, sizeof(uint32_t), 1, bin); + fwrite(&grid_height, sizeof(uint32_t), 1, bin); + } for (i=0; i>1)-4; x<=(single_channel_width>>1)+4; x++) - { - for (y=(single_channel_height>>1)-4; y<=(single_channel_height>>1)+4; y++) - { - mid_value_avg += channel[x + y*single_channel_width]; - count++; - } - } - uint16_t middle_val = (mid_value_avg / count) << 5; - + uint16_t *line; const char *channel_comments[4] = { "R", "Gr", "Gb", "B" }; - printf("Middle_val is %d\n", middle_val); - fprintf(header, "//%s - Ch %d\n", channel_comments[i], channel_ordering[bayer_order][i]); - uint16_t *line; - for (y=16; y=single_channel_height) - line = &channel[(single_channel_height-1)*(single_channel_width)]; - else - line = &channel[y*(single_channel_width)]; - - for(x=16; x= single_channel_height) + y_start = single_channel_height-1; + int y_stop = y_start+block_size; + if (y_stop > single_channel_height) + y_stop = single_channel_height; + + for (x=0; x 255) - gain = 255; //Clip as uint8_t - else if (gain < 32) - gain = 32; //Clip at x1.0 - fprintf(header, "%d, ", gain ); + int x_start = x*32+16-block_size/2; + if (x_start >= single_channel_width) + x_start = single_channel_width-1; + int x_stop = x_start+block_size; + if (x_stop > single_channel_width) + x_stop = single_channel_width; + + uint32_t block_val = 0; + uint16_t block_px = 0; + + for (int y_px = y_start; y_px < y_stop; y_px++) + { + line = &channel[y_px*(single_channel_width)]; + for (int x_px = x_start; x_px < x_stop; x_px++) + { + block_val += line[x_px]; + block_px++; + } + } + if (block_px < block_px_max) + block_val = block_val * block_px_max / block_px; // Scale sum in case of small edge blocks + + block_sum[block_idx++] = block_val ? block_val : 1; + if (block_val > max_blk_val) + max_blk_val = block_val; } - //Compute edge value from the very edge 2 pixels. + } + + max_blk_val <<= 5; + if (out_frmt&0x01) + { + fprintf(header, "//%s - Ch %d\n", channel_comments[i], channel_ordering[bayer_order][i]); + } + + // Calculate gain for each block + block_idx = 0; + for (y=0; y 255) - gain = 255; //Clip as uint8_t + gain = 255; //Clip as uint8_t else if (gain < 32) - gain = 32; //Clip at x1.0 - fprintf(header, "%d,\n", gain ); + gain = 32; //Clip at x1.0, should never happen + if (out_frmt&0x01) + { + fprintf(header, "%d, ", gain); + } + if (out_frmt&0x02) + { + uint8_t gain_bin = gain; + fwrite(&gain_bin, sizeof(uint8_t), 1, bin); + } + if (out_frmt&0x04) + { + fprintf(table, "%d %d %d %d\n", x * 32 + 16, y * 32 + 16, gain, i); + } } } } - fprintf(header, "};\n"); - fprintf(header, "uint32_t ref_transform = %u;\n", hdr->transform); - fprintf(header, "uint32_t grid_width = %u;\n", (single_channel_width>>5)+1); - fprintf(header, "uint32_t grid_height = %u;\n", (single_channel_height>>5)+1); + if (out_frmt&0x01) + { + fprintf(header, "};\n"); + fprintf(header, "uint32_t ref_transform = %u;\n", hdr->transform); + fprintf(header, "uint32_t grid_width = %u;\n", grid_width); + fprintf(header, "uint32_t grid_height = %u;\n", grid_height); + } for (i=0; i